Compare commits

...

65 Commits

Author SHA1 Message Date
David Heinemeier Hansson
e724e3ede6 Actually, removing File/FileUtil entirely is the answer 2008-09-04 16:00:28 +02:00
David Heinemeier Hansson
14995e1ae0 Use FileUtil for cp 2008-09-04 15:47:36 +02:00
David Heinemeier Hansson
61faf896c0 A few updates, still in progress 2008-09-04 15:30:31 +02:00
David Heinemeier Hansson
e8bf0651b6 Use personal server as temporary gem testing site until wrath get its shit together 2008-09-02 18:33:08 +02:00
David Heinemeier Hansson
a5d289776d Tell about the REXML fix in the CHANGELOG 2008-09-02 18:30:56 +02:00
David Heinemeier Hansson
e5c52a3a6e Prepare for 2.0.4 release 2008-09-02 16:49:32 +02:00
Michael Koziarski
9b251b684e Merge rexml-expansion-fix gem into activesupport.
Addresses the security issue documented at:
* http://www.ruby-lang.org/en/news/2008/08/23/dos-vulnerability-in-rexml/
2008-09-02 16:27:02 +02:00
Lars Kanis
79c3f73a7f PostgreSQL: fix quote_string for certain old pg drivers. [#94 state:resolved] 2008-08-27 22:51:46 -07:00
Jeremy Kemper
d356987e26 Fix typo in apparently-dead will_unload? method. 2008-06-27 17:20:46 -07:00
Michael Koziarski
44d721c193 Prepare for 2.0.3 2008-05-12 11:51:00 +12:00
Frederick Cheung
642da856d5 Deprecate AR::Base#attributes argument
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
2008-05-12 00:26:15 +01:00
Pratik Naik
63edc022f1 Deprecate ivars in view.
Deprecate use of @logger and @action_name instance variables inside
views. Please use instance methods logger and action_name instead.
2008-05-06 11:43:33 +01:00
Jeremy Kemper
2c96f509a8 Merge [9124] from trunk: Avoid remote_ip spoofing.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@9125 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-03-28 21:50:12 +00:00
Jeremy Kemper
1c207dfbfc Merge [9069] from trunk: Fix an edge case with extra periods in Routing.normalize_paths. References #11337.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@9070 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-03-21 22:46:51 +00:00
Jeremy Kemper
5a5b0b8b3b Merge [9056] from trunk: Migrations: create_table supports primary_key_prefix_type. References #10314.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@9057 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-03-18 18:36:11 +00:00
Rick Olson
b96db52878 port [9018] to 2.0 stable
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@9019 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-03-13 03:12:23 +00:00
Michael Koziarski
0e57097437 2-0-stable: Add OrderedHash#to_hash References #11266 [josh]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8973 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-03-03 03:30:57 +00:00
Michael Koziarski
0a1deae366 2-0-stable: Revert [8866]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8947 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-29 04:36:54 +00:00
Michael Koziarski
833d3e46ab 2-0-stable: Enumerable#group_by uses ActiveSupport::OrderedHash
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8946 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-29 04:28:51 +00:00
Michael Koziarski
6a386ea706 2-0-stable: Fix Fixtures for ActionController tests with fixtures. Closes #11162 [menno.sman]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8910 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-20 03:18:45 +00:00
Rick Olson
9d03813e91 apply [8865] to 2.0 stable
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8866 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-14 07:27:39 +00:00
Michael Koziarski
5b714e7197 2-0-stable: Remove table quoting; only pull last_insert_id if there's a primary key.
Merging [8663]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8860 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-12 20:11:50 +00:00
Michael Koziarski
f0000281ff 2-0-stable: Show RecordInvalid in the documentation. Closes #10976 [kampers]
Merging [8845]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8846 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 02:50:13 +00:00
Michael Koziarski
41adf8710e 2-0-stable: Add documentation for polymorphic URL helpers, make API consistent for polymorphic_path and polymorphic_url.
References #10883 [mislav] References #8782 [gbuesing] References #8720 [gbuesing]

Merging [8741]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8844 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:14:10 +00:00
Michael Koziarski
2dbceab361 Don't split cookies on comma also. References r8505.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8843 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:13:33 +00:00
Michael Koziarski
a550d2aadb 2-0-stable: Correct inconsistencies in RequestForgeryProtection docs. References #11032 [mislav]
Merging [8807]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8842 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:12:44 +00:00
Michael Koziarski
a77e57f4e3 2-0-stable: Make the docs reference the correct function. [Tobi Reif]
Merging [8802]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8841 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:12:13 +00:00
Michael Koziarski
e1b3a441ed 2-0-stable: Less verbose mail logging: just recipients for :info log level; the whole email for :debug only. References #8000.
Merging [8781]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8840 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:11:45 +00:00
Michael Koziarski
8b63dd09c0 2-0-stable: When multiparameter date assignment fails due to an invalid date, fall back to create a Time and convert to_date. References #10556 [leikind]
Merging [8777]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8839 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:10:43 +00:00
Michael Koziarski
0dad92a790 2-0-stable: Make the non inferrable controller message a little friendlier. [Koz]
Merging [8749]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8838 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:10:14 +00:00
Michael Koziarski
e3a39ca91d Rejig strange changelog error
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8837 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:09:47 +00:00
Michael Koziarski
8e556e5d74 2-0-stable: Make assert_routing aware of the HTTP method used. References #8039 [mpalmer]
Merging [8748]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8836 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:09:07 +00:00
Michael Koziarski
a9113b8895 2-0-stable: Precalculate the Backtrace of a template error to avoid raising exceptions in TemplateError#backtrace. These will abort the ruby vm. References #10936 [Aleksey Kondratenko]
Merging [8743]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8835 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:08:05 +00:00
Michael Koziarski
b1aa67410a 2-0-stable: Don't repeatedly add relative_url_root to asset sources. References #10767 [tomtoday, Koz]
Merging [8740]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8834 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:07:24 +00:00
Michael Koziarski
291dad2411 2-0-stable: Correct documentation for dom_id [jbarnette] References #10775
Merging [8734]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8833 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:06:34 +00:00
Michael Koziarski
0f225c0f3e 2-0-stable: Add tests and documentation for allow_blank. References #10651 [blj]
Merging [8733]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8832 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:05:54 +00:00
Michael Koziarski
75aef09ee1 2-0-stable: Reference to :element option in documentation should be :member. References #10905 [fxn]
Merging [8706]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8831 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:05:04 +00:00
Michael Koziarski
c71de03306 2-0-stable: Add label_tag helper for generating elements. References #10802 [DefV]
Merging [8685]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8830 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:04:27 +00:00
Michael Koziarski
81991d6913 2-0-stable: Ensure mime types can be compared with symbols. References #10796 [bscofield]
Merging [8677]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8829 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:03:27 +00:00
Michael Koziarski
68477ad16d 2-0-stable: Ensure that modifying has_and_belongs_to_many actions clear the query cache. References #10840 [john.andrews] Merging [8653]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8828 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-10 01:02:38 +00:00
Rick Olson
569a78cbe0 Apply [8810], [8811], [8812], and [8813] to 2.0 stable
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8814 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-07 17:11:04 +00:00
Michael Koziarski
2a07886be3 Ensure that assert_redirected_to to top-level named route from namespaced controller work. Closes #10812 [dasil003]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8801 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-04 23:26:25 +00:00
Jeremy Kemper
36fa00a1eb Merge r8782 from trunk: TestSession supports indifferent access. References #7372.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8783 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-02 05:34:28 +00:00
Jeremy Kemper
c31a04aba3 Merge r8778 from trunk: fix calculations on associations with custom :foreign_key. References #8117.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8779 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-02 04:56:11 +00:00
Jeremy Kemper
f09a529138 Merge r8774 from trunk: fix number_to_human_size incorrectly removing trailing zeros. References #10099.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8775 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-02-02 03:00:25 +00:00
Michael Koziarski
6997918a9d Correct docs on caching and all.js. Merging [8641]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8642 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-14 04:24:16 +00:00
Michael Koziarski
f3b382692a Merge the fix for redirecting to nil. References #10272 [farleyknight]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8634 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-12 03:10:38 +00:00
Michael Koziarski
1752dccb34 Merge [8627] fix to stable.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8631 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-12 01:33:02 +00:00
Jeremy Kemper
207473bed8 Merge [8622] to stable. References #10766.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8623 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-11 02:16:58 +00:00
Rick Olson
3a90e72e6f Updated TMail to version 1.2.1 (2.0 stable) [raasdnil]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8621 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-11 01:57:23 +00:00
Jeremy Kemper
ca66f4471f Merge [8616] to stable: UrlWriter respects relative_url_root. References #10748.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8618 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-10 02:53:33 +00:00
Jeremy Kemper
e2ce901a78 Merge [8615] to stable: resurrect WordNet synonym lookups. References #10710.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8617 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-10 02:52:06 +00:00
Jeremy Kemper
3006870c1a Merge [8608] to stable: simplify to_formatted_s docs. References #10747.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8609 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-09 09:49:12 +00:00
Jeremy Kemper
3328171a40 Merge [8606] to stable: document date and time to_formatted_s. References #10747.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8607 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-09 09:04:35 +00:00
Jeremy Kemper
09b1021b99 Merge [8604] to stable: fix up Enumerable#group_by
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8605 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-09 08:40:07 +00:00
Jeremy Kemper
9b31893668 Merge [8602] to stable: observed_subclasses gives [] if there are no observed classes
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8603 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-09 06:12:10 +00:00
Michael Koziarski
002e73a1ee Don't append the forgery token to an ajax request if it's serializing a form, prevents duplicate tokens. References #10684 [macournoyer]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8601 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-08 21:22:01 +00:00
Michael Koziarski
b2eca732ba Merge labelled_form_for example. Closes #10738 [justinfrench, Koz]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8600 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-08 21:20:41 +00:00
Michael Koziarski
274ef21104 Merge classify documentation. Closes #10615 [kris_chambers]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8599 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-08 21:20:04 +00:00
Rick Olson
24abd43d56 Doc fixes for #atom_feed_helper. #10715 [hasmanyjosh]
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8592 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-07 20:43:56 +00:00
Michael Koziarski
083b0b7f3f Catchup Backport to 2.0 of the following Changesets:
[8489]
[8491]
[8496]
[8502]
[8503]
[8504]
[8505]
[8506]
[8508]
[8516]
[8521]
[8522]
[8525]
[8526]
[8527]
[8528]
[8529]
[8531]
[8532]
[8533]
[8534]
[8535]
[8536]
[8539]
[8541]
[8543]
[8554]
[8556]
[8558]
[8560]
[8561]
[8562]
[8576]
[8577]
[8581]
[8583]


git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8585 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-07 05:29:15 +00:00
Michael Koziarski
fa7e937e67 Merge CSV compat changes to stable, closes #10673
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8545 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-03 19:32:33 +00:00
Jeremy Kemper
131acec685 Merge [8519] from trunk: SQLite: db:drop:all doesn't fail silently if the database is already open. References #10577.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8520 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2008-01-02 08:34:44 +00:00
Jeremy Kemper
c48a1fc213 Merge r8497 from trunk: ensure that test case setup is run even if overridden. References #10382.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8498 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2007-12-28 05:27:11 +00:00
Jeremy Kemper
9bd987f28b Branching for stable 2.0.x line of development just before addition of native mongrel handler.
git-svn-id: http://svn-commit.rubyonrails.org/rails/branches/2-0-stable@8495 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
2007-12-28 03:51:10 +00:00
147 changed files with 2964 additions and 1211 deletions

View File

@@ -1,3 +1,12 @@
*2.0.4* (2nd September 2008)
* Less verbose mail logging: just recipients for :info log level; the whole email for :debug only. #8000 [iaddict, Tarmo Tänav]
* Updated TMail to version 1.2.1 [raasdnil]
* Fixed that you don't have to call super in ActionMailer::TestCase#setup #10406 [jamesgolick]
*2.0.2* (December 16th, 2007)
* Included in Rails 2.0.2

View File

@@ -4,6 +4,7 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
require 'rake/contrib/sshpublisher'
require 'rake/contrib/rubyforgepublisher'
require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
@@ -55,7 +56,7 @@ spec = Gem::Specification.new do |s|
s.rubyforge_project = "actionmailer"
s.homepage = "http://www.rubyonrails.org"
s.add_dependency('actionpack', '= 2.0.2' + PKG_BUILD)
s.add_dependency('actionpack', '= 2.0.4' + PKG_BUILD)
s.has_rdoc = true
s.requirements << 'none'
@@ -76,7 +77,8 @@ end
desc "Publish the API documentation"
task :pgem => [:package] do
Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
Rake::SshFilePublisher.new("david@greed.loudthinking.com", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
`ssh david@greed.loudthinking.com '/u/sites/gems/gemupdate.sh'`
end
desc "Publish the API documentation"

View File

@@ -463,7 +463,10 @@ module ActionMailer #:nodoc:
# no alternate has been given as the parameter, this will fail.
def deliver!(mail = @mail)
raise "no mail object available for delivery!" unless mail
logger.info "Sent mail:\n #{mail.encoded}" unless logger.nil?
unless logger.nil?
logger.info "Sent mail to #{recipients.to_a.join(', ')}"
logger.debug "\n#{mail.encoded}"
end
begin
__send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries

View File

@@ -33,7 +33,7 @@ module ActionMailer
end
end
def setup
def setup_with_mailer
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.deliveries = []
@@ -42,6 +42,19 @@ module ActionMailer
@expected.set_content_type "text", "plain", { "charset" => charset }
@expected.mime_version = '1.0'
end
alias_method :setup, :setup_with_mailer
def self.method_added(method)
if method.to_s == 'setup'
unless method_defined?(:setup_without_mailer)
alias_method :setup_without_mailer, :setup
define_method(:setup) do
setup_with_mailer
setup_without_mailer
end
end
end
end
private
def charset

View File

@@ -2,9 +2,9 @@
require 'rubygems'
begin
gem 'tmail', '~> 1.1.0'
gem 'tmail', '~> 1.2.1'
rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/tmail-1.1.0"
$:.unshift "#{File.dirname(__FILE__)}/vendor/tmail-1.2.1"
end
begin

View File

@@ -1,540 +0,0 @@
=begin rdoc
= Facade.rb Provides an interface to the TMail object
=end
#--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
# with permission of Minero Aoki.
#++
require 'tmail/utils'
module TMail
class Mail
def header_string( name, default = nil )
h = @header[name.downcase] or return default
h.to_s
end
###
### attributes
###
include TextUtils
def set_string_array_attr( key, strs )
strs.flatten!
if strs.empty?
@header.delete key.downcase
else
store key, strs.join(', ')
end
strs
end
private :set_string_array_attr
def set_string_attr( key, str )
if str
store key, str
else
@header.delete key.downcase
end
str
end
private :set_string_attr
def set_addrfield( name, arg )
if arg
h = HeaderField.internal_new(name, @config)
h.addrs.replace [arg].flatten
@header[name] = h
else
@header.delete name
end
arg
end
private :set_addrfield
def addrs2specs( addrs )
return nil unless addrs
list = addrs.map {|addr|
if addr.address_group?
then addr.map {|a| a.spec }
else addr.spec
end
}.flatten
return nil if list.empty?
list
end
private :addrs2specs
#
# date time
#
def date( default = nil )
if h = @header['date']
h.date
else
default
end
end
def date=( time )
if time
store 'Date', time2str(time)
else
@header.delete 'date'
end
time
end
def strftime( fmt, default = nil )
if t = date
t.strftime(fmt)
else
default
end
end
#
# destination
#
def to_addrs( default = nil )
if h = @header['to']
h.addrs
else
default
end
end
def cc_addrs( default = nil )
if h = @header['cc']
h.addrs
else
default
end
end
def bcc_addrs( default = nil )
if h = @header['bcc']
h.addrs
else
default
end
end
def to_addrs=( arg )
set_addrfield 'to', arg
end
def cc_addrs=( arg )
set_addrfield 'cc', arg
end
def bcc_addrs=( arg )
set_addrfield 'bcc', arg
end
def to( default = nil )
addrs2specs(to_addrs(nil)) || default
end
def cc( default = nil )
addrs2specs(cc_addrs(nil)) || default
end
def bcc( default = nil )
addrs2specs(bcc_addrs(nil)) || default
end
def to=( *strs )
set_string_array_attr 'To', strs
end
def cc=( *strs )
set_string_array_attr 'Cc', strs
end
def bcc=( *strs )
set_string_array_attr 'Bcc', strs
end
#
# originator
#
def from_addrs( default = nil )
if h = @header['from']
h.addrs
else
default
end
end
def from_addrs=( arg )
set_addrfield 'from', arg
end
def from( default = nil )
addrs2specs(from_addrs(nil)) || default
end
def from=( *strs )
set_string_array_attr 'From', strs
end
def friendly_from( default = nil )
h = @header['from']
a, = h.addrs
return default unless a
return a.phrase if a.phrase
return h.comments.join(' ') unless h.comments.empty?
a.spec
end
def reply_to_addrs( default = nil )
if h = @header['reply-to']
h.addrs
else
default
end
end
def reply_to_addrs=( arg )
set_addrfield 'reply-to', arg
end
def reply_to( default = nil )
addrs2specs(reply_to_addrs(nil)) || default
end
def reply_to=( *strs )
set_string_array_attr 'Reply-To', strs
end
def sender_addr( default = nil )
f = @header['sender'] or return default
f.addr or return default
end
def sender_addr=( addr )
if addr
h = HeaderField.internal_new('sender', @config)
h.addr = addr
@header['sender'] = h
else
@header.delete 'sender'
end
addr
end
def sender( default )
f = @header['sender'] or return default
a = f.addr or return default
a.spec
end
def sender=( str )
set_string_attr 'Sender', str
end
#
# subject
#
def subject( default = nil )
if h = @header['subject']
h.body
else
default
end
end
alias quoted_subject subject
def subject=( str )
set_string_attr 'Subject', str
end
#
# identity & threading
#
def message_id( default = nil )
if h = @header['message-id']
h.id || default
else
default
end
end
def message_id=( str )
set_string_attr 'Message-Id', str
end
def in_reply_to( default = nil )
if h = @header['in-reply-to']
h.ids
else
default
end
end
def in_reply_to=( *idstrs )
set_string_array_attr 'In-Reply-To', idstrs
end
def references( default = nil )
if h = @header['references']
h.refs
else
default
end
end
def references=( *strs )
set_string_array_attr 'References', strs
end
#
# MIME headers
#
def mime_version( default = nil )
if h = @header['mime-version']
h.version || default
else
default
end
end
def mime_version=( m, opt = nil )
if opt
if h = @header['mime-version']
h.major = m
h.minor = opt
else
store 'Mime-Version', "#{m}.#{opt}"
end
else
store 'Mime-Version', m
end
m
end
def content_type( default = nil )
if h = @header['content-type']
h.content_type || default
else
default
end
end
def main_type( default = nil )
if h = @header['content-type']
h.main_type || default
else
default
end
end
def sub_type( default = nil )
if h = @header['content-type']
h.sub_type || default
else
default
end
end
def set_content_type( str, sub = nil, param = nil )
if sub
main, sub = str, sub
else
main, sub = str.split(%r</>, 2)
raise ArgumentError, "sub type missing: #{str.inspect}" unless sub
end
if h = @header['content-type']
h.main_type = main
h.sub_type = sub
h.params.clear
else
store 'Content-Type', "#{main}/#{sub}"
end
@header['content-type'].params.replace param if param
str
end
alias content_type= set_content_type
def type_param( name, default = nil )
if h = @header['content-type']
h[name] || default
else
default
end
end
def charset( default = nil )
if h = @header['content-type']
h['charset'] or default
else
default
end
end
def charset=( str )
if str
if h = @header[ 'content-type' ]
h['charset'] = str
else
store 'Content-Type', "text/plain; charset=#{str}"
end
end
str
end
def transfer_encoding( default = nil )
if h = @header['content-transfer-encoding']
h.encoding || default
else
default
end
end
def transfer_encoding=( str )
set_string_attr 'Content-Transfer-Encoding', str
end
alias encoding transfer_encoding
alias encoding= transfer_encoding=
alias content_transfer_encoding transfer_encoding
alias content_transfer_encoding= transfer_encoding=
def disposition( default = nil )
if h = @header['content-disposition']
h.disposition || default
else
default
end
end
alias content_disposition disposition
def set_disposition( str, params = nil )
if h = @header['content-disposition']
h.disposition = str
h.params.clear
else
store('Content-Disposition', str)
h = @header['content-disposition']
end
h.params.replace params if params
end
alias disposition= set_disposition
alias set_content_disposition set_disposition
alias content_disposition= set_disposition
def disposition_param( name, default = nil )
if h = @header['content-disposition']
h[name] || default
else
default
end
end
###
### utils
###
def create_reply
mail = TMail::Mail.parse('')
mail.subject = 'Re: ' + subject('').sub(/\A(?:\[[^\]]+\])?(?:\s*Re:)*\s*/i, '')
mail.to_addrs = reply_addresses([])
mail.in_reply_to = [message_id(nil)].compact
mail.references = references([]) + [message_id(nil)].compact
mail.mime_version = '1.0'
mail
end
def base64_encode
store 'Content-Transfer-Encoding', 'Base64'
self.body = Base64.folding_encode(self.body)
end
def base64_decode
if /base64/i === self.transfer_encoding('')
store 'Content-Transfer-Encoding', '8bit'
self.body = Base64.decode(self.body, @config.strict_base64decode?)
end
end
def destinations( default = nil )
ret = []
%w( to cc bcc ).each do |nm|
if h = @header[nm]
h.addrs.each {|i| ret.push i.address }
end
end
ret.empty? ? default : ret
end
def each_destination( &block )
destinations([]).each do |i|
if Address === i
yield i
else
i.each(&block)
end
end
end
alias each_dest each_destination
def reply_addresses( default = nil )
reply_to_addrs(nil) or from_addrs(nil) or default
end
def error_reply_addresses( default = nil )
if s = sender(nil)
[s]
else
from_addrs(default)
end
end
def multipart?
main_type('').downcase == 'multipart'
end
end # class Mail
end # module TMail

View File

@@ -1,36 +1,35 @@
=begin rdoc
= Text Encoding class
=end
#--
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
# = TITLE:
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# Text Encoding class
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# = COPYRIGHT:
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
#
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
# with permission of Minero Aoki.
#++
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
# with permission of Minero Aoki.
require 'nkf'
require 'tmail/base64.rb'
require 'tmail/base64'
require 'tmail/stringio'
require 'tmail/utils'
@@ -52,25 +51,25 @@ module TMail
end
end
module_function :create_dest
def encoded( eol = "\r\n", charset = 'j', dest = nil )
accept_strategy Encoder, eol, charset, dest
end
def decoded( eol = "\n", charset = 'e', dest = nil )
# Turn the E-Mail into a string and return it with all
# encoded characters decoded. alias for to_s
accept_strategy Decoder, eol, charset, dest
end
alias to_s decoded
def accept_strategy( klass, eol, charset, dest = nil )
dest ||= ''
accept klass.new( create_dest(dest), charset, eol )
dest
end
end
@@ -121,7 +120,7 @@ module TMail
def header_body( str )
@f << decode(str)
end
def space
@f << ' '
end
@@ -131,7 +130,7 @@ module TMail
def lwsp( str )
@f << str
end
def meta( str )
@f << str
end
@@ -182,7 +181,8 @@ module TMail
end
SPACER = "\t"
MAX_LINE_LEN = 70
MAX_LINE_LEN = 78
RFC_2822_MAX_LENGTH = 998
OPTIONS = {
'EUC' => '-Ej -m0',
@@ -202,7 +202,7 @@ module TMail
def preserve_quotes=( bool )
@preserve_quotes
end
def preserve_quotes
@preserve_quotes
end
@@ -378,7 +378,7 @@ module TMail
i += 1
end
end
METHOD_ID = {
?j => :extract_J,
?e => :extract_E,
@@ -451,31 +451,73 @@ module TMail
# puts '---- lwsp -------------------------------------'
# puts "+ #{lwsp.inspect}"
fold if restsize() <= 0
flush
flush(@folded)
@lwsp = lwsp
end
def flush
def flush(folded = false)
# puts '---- flush ----'
# puts "spc >>>#{@lwsp.inspect}<<<"
# puts "txt >>>#{@text.inspect}<<<"
@f << @lwsp << @text
@curlen += (@lwsp.size + @text.size)
if folded
@curlen = 0
else
@curlen += (@lwsp.size + @text.size)
end
@text = ''
@lwsp = ''
end
def fold
# puts '---- fold ----'
@f << @eol
unless @f.string =~ /^.*?:$/
@f << @eol
@lwsp = SPACER
else
fold_header
@folded = true
end
@curlen = 0
@lwsp = SPACER
end
def fold_header
# Called because line is too long - so we need to wrap.
# First look for whitespace in the text
# if it has text, fold there
# check the remaining text, if too long, fold again
# if it doesn't, then don't fold unless the line goes beyond 998 chars
# Check the text to see if there is whitespace, or if not
@wrapped_text = []
until @text == ''
fold_the_string
end
@text = @wrapped_text.join("#{@eol}#{SPACER}")
end
def fold_the_string
whitespace_location = @text =~ /\s/ || @text.length
# Is the location of the whitespace shorter than the RCF_2822_MAX_LENGTH?
# if there is no whitespace in the string, then this
unless mazsize(whitespace_location) <= 0
@wrapped_text << @text.slice!(0...whitespace_location)
# If it is not less, we have to wrap it destructively
else
slice_point = RFC_2822_MAX_LENGTH - @curlen - @lwsp.length
@wrapped_text << @text.slice!(0...slice_point)
end
end
def restsize
MAX_LINE_LEN - (@curlen + @lwsp.size + @text.size)
end
def mazsize(whitespace_location)
# Per RFC2822, the maximum length of a line is 998 chars
RFC_2822_MAX_LENGTH - (@curlen + @lwsp.size + whitespace_location)
end
end
end # module TMail

View File

@@ -54,8 +54,40 @@ module TMail
klass.newobj body, conf
end
# Returns a HeaderField object matching the header you specify in the "name" param.
# Requires an initialized TMail::Port to be passed in.
#
# The method searches the header of the Port you pass into it to find a match on
# the header line you pass. Once a match is found, it will unwrap the matching line
# as needed to return an initialized HeaderField object.
#
# If you want to get the Envelope sender of the email object, pass in "EnvelopeSender",
# if you want the From address of the email itself, pass in 'From'.
#
# This is because a mailbox doesn't have the : after the From that designates the
# beginning of the envelope sender (which can be different to the from address of
# the emial)
#
# Other fields can be passed as normal, "Reply-To", "Received" etc.
#
# Note: Change of behaviour in 1.2.1 => returns nil if it does not find the specified
# header field, otherwise returns an instantiated object of the correct header class
#
# For example:
# port = TMail::FilePort.new("/test/fixtures/raw_email_simple")
# h = TMail::HeaderField.new_from_port(port, "From")
# h.addrs.to_s #=> "Mikel Lindsaar <mikel@nowhere.com>"
# h = TMail::HeaderField.new_from_port(port, "EvelopeSender")
# h.addrs.to_s #=> "mike@anotherplace.com.au"
# h = TMail::HeaderField.new_from_port(port, "SomeWeirdHeaderField")
# h #=> nil
def new_from_port( port, name, conf = DEFAULT_CONFIG )
re = Regep.new('\A(' + Regexp.quote(name) + '):', 'i')
if name == "EnvelopeSender"
name = "From"
re = Regexp.new('\A(From) ', 'i')
else
re = Regexp.new('\A(' + Regexp.quote(name) + '):', 'i')
end
str = nil
port.ropen {|f|
f.each do |line|
@@ -66,7 +98,7 @@ module TMail
end
end
}
new(name, str, Config.to_config(conf))
new(name, str, Config.to_config(conf)) if str
end
def internal_new( name, conf )

File diff suppressed because it is too large Load Diff

View File

@@ -28,6 +28,23 @@
# Note: Originally licensed under LGPL v2+. Using MIT license for Rails
# with permission of Minero Aoki.
#++
# == TMail::Mail
# === Class Methods
#
#
#
#
#
#
#
#
#
#
#
#
#
#
require 'tmail/interface'
require 'tmail/encode'
@@ -162,6 +179,14 @@ module TMail
@header.dup
end
# Returns a TMail::AddressHeader object of the field you are querying.
# Examples:
# @mail['from'] #=> #<TMail::AddressHeader "mikel@test.com.au">
# @mail['to'] #=> #<TMail::AddressHeader "mikel@test.com.au">
#
# You can get the string value of this by passing "to_s" to the query:
# Example:
# @mail['to'].to_s #=> "mikel@test.com.au"
def []( key )
@header[key.downcase]
end
@@ -172,6 +197,19 @@ module TMail
alias fetch []
# Allows you to set or delete TMail header objects at will.
# Eamples:
# @mail = TMail::Mail.new
# @mail['to'].to_s # => 'mikel@test.com.au'
# @mail['to'] = 'mikel@elsewhere.org'
# @mail['to'].to_s # => 'mikel@elsewhere.org'
# @mail.encoded # => "To: mikel@elsewhere.org\r\n\r\n"
# @mail['to'] = nil
# @mail['to'].to_s # => nil
# @mail.encoded # => "\r\n"
#
# Note: setting mail[] = nil actualy deletes the header field in question from the object,
# it does not just set the value of the hash to nil
def []=( key, val )
dkey = key.downcase
@@ -203,7 +241,14 @@ module TMail
end
alias store []=
# Allows you to loop through each header in the TMail::Mail object in a block
# Example:
# @mail['to'] = 'mikel@elsewhere.org'
# @mail['from'] = 'me@me.com'
# @mail.each_header { |k,v| puts "#{k} = #{v}" }
# # => from = me@me.com
# # => to = mikel@elsewhere.org
def each_header
@header.each do |key, val|
[val].flatten.each {|v| yield key, v }

View File

@@ -213,13 +213,13 @@ module TMail
fromaddr(), TextUtils.time2str(File.mtime(port.filename))
end
def UNIXMbox.fromaddr
def UNIXMbox.fromaddr(port)
h = HeaderField.new_from_port(port, 'Return-Path') ||
HeaderField.new_from_port(port, 'From') or return 'nobody'
HeaderField.new_from_port(port, 'From') ||
HeaderField.new_from_port(port, 'EnvelopeSender') or return 'nobody'
a = h.addrs[0] or return 'nobody'
a.spec
end
private_class_method :fromaddr
def close
return if @closed

View File

@@ -128,41 +128,6 @@ module TMail
'using C.T.Encoding with multipart mail is not permitted'
end
end
def create_empty_mail
self.class.new(StringPort.new(''), @config)
end
def create_reply
setup_reply create_empty_mail()
end
def setup_reply( m )
if tmp = reply_addresses(nil)
m.to_addrs = tmp
end
mid = message_id(nil)
tmp = references(nil) || []
tmp.push mid if mid
m.in_reply_to = [mid] if mid
m.references = tmp unless tmp.empty?
m.subject = 'Re: ' + subject('').sub(/\A(?:\s*re:)+/i, '')
m
end
def create_forward
setup_forward create_empty_mail()
end
def setup_forward( mail )
m = Mail.new(StringPort.new(''))
m.body = decoded
m.set_content_type 'message', 'rfc822'
m.encoding = encoding('7bit')
mail.parts.push m
end
end

View File

@@ -13,12 +13,12 @@ module TMail
from_charset = sub_header("content-type", "charset")
case (content_transfer_encoding || "7bit").downcase
when "quoted-printable"
# the default charset is set to iso-8859-1 instead of 'us-ascii'.
# the default charset is set to iso-8859-1 instead of 'us-ascii'.
# This is needed as many mailer do not set the charset but send in ISO. This is only used if no charset is set.
if !from_charset.blank? && from_charset.downcase == 'us-ascii'
from_charset = 'iso-8859-1'
end
Unquoter.unquote_quoted_printable_and_convert_to(quoted_body,
to_charset, from_charset, true)
when "base64"
@@ -35,9 +35,9 @@ module TMail
def body(to_charset = 'utf-8', &block)
attachment_presenter = block || Proc.new { |file_name| "Attachment: #{file_name}\n" }
if multipart?
parts.collect { |part|
parts.collect { |part|
header = part["content-type"]
if part.multipart?
@@ -81,13 +81,13 @@ module TMail
end
end
end
def unquote_quoted_printable_and_convert_to(text, to, from, preserve_underscores=false)
text = text.gsub(/_/, " ") unless preserve_underscores
text = text.gsub(/\r\n|\r/, "\n") # normalize newlines
convert_to(text.unpack("M*").first, to, from)
end
def unquote_base64_and_convert_to(text, to, from)
convert_to(Base64.decode(text), to, from)
end
@@ -116,27 +116,3 @@ module TMail
end
end
end
if __FILE__ == $0
require 'test/unit'
class TC_Unquoter < Test::Unit::TestCase
def test_unquote_quoted_printable
a ="=?ISO-8859-1?Q?[166417]_Bekr=E6ftelse_fra_Rejsefeber?="
b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
end
def test_unquote_base64
a ="=?ISO-8859-1?B?WzE2NjQxN10gQmVrcuZmdGVsc2UgZnJhIFJlanNlZmViZXI=?="
b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
assert_equal "[166417] Bekr\303\246ftelse fra Rejsefeber", b
end
def test_unquote_without_charset
a ="[166417]_Bekr=E6ftelse_fra_Rejsefeber"
b = TMail::Unquoter.unquote_and_convert_to(a, 'utf-8')
assert_equal "[166417]_Bekr=E6ftelse_fra_Rejsefeber", b
end
end
end

View File

@@ -30,14 +30,18 @@
#++
require 'tmail/utils'
require 'tmail/config'
module TMail
require 'tmail/scanner_r.rb'
# NOTE: It woiuld be nice if these two libs could boith be called "tmailscanner", and
# the native extension would have precedence. However RubyGems boffs that up b/c
# it does not gaurantee load_path order.
begin
raise LoadError, 'Turn off Ruby extention by user choice' if ENV['NORUBYEXT']
require 'tmail/scanner_c.so'
Scanner = Scanner_C
raise LoadError, 'Turned off native extentions by user choice' if ENV['NORUBYEXT']
require('tmail/tmailscanner') # c extension
Scanner = TMailScanner
rescue LoadError
Scanner = Scanner_R
require 'tmail/scanner_r'
Scanner = TMailScanner
end
end

View File

@@ -29,12 +29,11 @@
require 'tmail/config'
module TMail
class Scanner_R
class TMailScanner
Version = '0.10.7'
Version = '0.11.0'
Version.freeze
MIME_HEADERS = {
@@ -46,7 +45,6 @@ module TMail
alnum = 'a-zA-Z0-9'
atomsyms = %q[ _#!$%&`'*+-{|}~^/=? ].strip
tokensyms = %q[ _#!$%&`'*+-{|}~^@. ].strip
atomchars = alnum + Regexp.quote(atomsyms)
tokenchars = alnum + Regexp.quote(tokensyms)
iso2022str = '\e(?!\(B)..(?:[^\e]+|\e(?!\(B)..)*\e\(B'

View File

@@ -56,7 +56,7 @@ module TMail
module TextUtils
# Defines characters per RFC that are OK for TOKENs, ATOMs, PHRASEs and CONTROL characters.
aspecial = '()<>[]:;.\\,"'
tspecial = '()<>[];:\\,"/?='
lwsp = " \t\r\n"
@@ -248,8 +248,7 @@ module TMail
def decode_RFC2231( str )
m = RFC2231_ENCODED.match(str) or return str
begin
NKF.nkf(NKF_FLAGS[$KCODE],
m.post_match.gsub(/%[\da-f]{2}/in) {|s| s[1,2].hex.chr })
to_kcode(m.post_match.gsub(/%[\da-f]{2}/in) {|s| s[1,2].hex.chr })
rescue
m.post_match.gsub(/%[\da-f]{2}/in, "")
end
@@ -263,7 +262,7 @@ module TMail
preamble = $1
remainder = $2
if remainder =~ /;/
remainder =~ /^(.*)(;.*)$/m
remainder =~ /^(.*?)(;.*)$/m
boundary_text = $1
post = $2.chomp
else

View File

@@ -30,8 +30,8 @@
module TMail #:nodoc:
module VERSION #:nodoc:
MAJOR = 1
MINOR = 1
TINY = 1
MINOR = 2
TINY = 0
STRING = [MAJOR, MINOR, TINY].join('.')
end

View File

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

View File

@@ -534,7 +534,8 @@ class ActionMailerTest < Test::Unit::TestCase
def test_delivery_logs_sent_mail
mail = TestMailer.create_signed_up(@recipient)
logger = mock()
logger.expects(:info).with("Sent mail:\n #{mail.encoded}")
logger.expects(:info).with("Sent mail to #{@recipient}")
logger.expects(:debug).with("\n#{mail.encoded}")
TestMailer.logger = logger
TestMailer.deliver_signed_up(@recipient)
end
@@ -838,7 +839,7 @@ EOF
fixture = File.read(File.dirname(__FILE__) + "/fixtures/raw_email8")
mail = TMail::Mail.parse(fixture)
attachment = mail.attachments.last
assert_equal "01QuienTeDijat.Pitbull.mp3", attachment.original_filename
assert_equal "01 Quien Te Dij\212at. Pitbull.mp3", attachment.original_filename
end
def test_wrong_mail_header

View File

@@ -115,3 +115,21 @@ class TestHelperMailerTest < ActionMailer::TestCase
assert_match /0 .* but 1/, error.message
end
end
class AnotherTestHelperMailerTest < ActionMailer::TestCase
tests TestHelperMailer
def setup
# Should not override ActionMailer setup methods
@test_var = "a value"
end
def test_should_still_setup_mailer
assert @expected.is_a?(TMail::Mail)
end
def test_should_run_overridden_setup_method
assert @test_var
end
end

View File

@@ -1,4 +1,42 @@
*SVN*
*2.0.4* (2nd September 2008)
* Avoid remote_ip spoofing. [Brian Candler]
* Correct inconsistencies in RequestForgeryProtection docs. #11032 [mislav]
* Make assert_routing aware of the HTTP method used. #8039 [mpalmer]
e.g. assert_routing({ :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" })
* Remove ERB trim variables from trace template in case ActionView::Base.erb_trim_mode is changed in the application. #10098 [tpope, kampers]
* Fix typo in form_helper documentation. #10650 [xaviershay, kampers]
* Fix bug with setting Request#format= after the getter has cached the value. #10889 [cch1]
* Add label_tag helper for generating elements. #10802 [DefV]
* TestSession supports indifferent access. #7372 [tamc, Arsen7, mhackett, julik, jean.helou]
* UrlWriter respects relative_url_root. #10748 [Cheah Chu Yeow]
* Support render :text => nil. #6684 [tjennings, PotatoSalad, Cheah Chu Yeow]
* assert_response failures include the exception message. #10688 [Seth Rasmussen]
* Fixed rendering of partials with layout when done from site layout #9209 [antramm]
* Fix atom_feed_helper to comply with the atom spec. Closes #10672 [xaviershay]
* The tags created do not contain a date (http://feedvalidator.org/docs/error/InvalidTAG.html)
* IDs are not guaranteed unique
* A default self link was not provided, contrary to the documentation
* NOTE: This changes tags for existing atom entries, but at least they validate now.
* Correct indentation in tests. Closes #10671 [l.guidi]
* Fix that auto_link looks for ='s in url paths (Amazon urls have them). Closes #10640 [bgreenlee]
* Ensure that test case setup is run even if overridden. #10382 [Josh Peek]
* Fix HTML Sanitizer to allow trailing spaces in CSS style attributes. Closes #10566 [wesley.moxam]

View File

@@ -4,6 +4,7 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
require 'rake/contrib/sshpublisher'
require 'rake/contrib/rubyforgepublisher'
require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version')
@@ -76,7 +77,7 @@ spec = Gem::Specification.new do |s|
s.has_rdoc = true
s.requirements << 'none'
s.add_dependency('activesupport', '= 2.0.2' + PKG_BUILD)
s.add_dependency('activesupport', '= 2.0.4' + PKG_BUILD)
s.require_path = 'lib'
s.autorequire = 'action_controller'
@@ -132,8 +133,8 @@ task :update_js => [ :update_scriptaculous ]
desc "Publish the API documentation"
task :pgem => [:package] do
Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
`ssh davidhh@wrath.rubyonrails.org './gemupdate.sh'`
Rake::SshFilePublisher.new("david@greed.loudthinking.com", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
`ssh david@greed.loudthinking.com '/u/sites/gems/gemupdate.sh'`
end
desc "Publish the API documentation"
@@ -144,6 +145,7 @@ end
desc "Publish the release files to RubyForge."
task :release => [ :package ] do
require 'rubyforge'
require 'rake/contrib/rubyforgepublisher'
packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }

View File

@@ -33,7 +33,11 @@ module ActionController
elsif type.is_a?(Symbol) && @response.response_code == ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[type]
assert_block("") { true } # to count the assertion
else
assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
if @response.error?
assert_block(build_message(message, "Expected response to be a <?>, but was <?>\n<?>", type, @response.response_code, @response.template.instance_variable_get(:@exception).message)) { false }
else
assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
end
end
end
end
@@ -91,7 +95,7 @@ module ActionController
value['controller'] = value['controller'].to_s
if key == :actual && value['controller'].first != '/' && !value['controller'].include?('/')
new_controller_path = ActionController::Routing.controller_relative_to(value['controller'], @controller.class.controller_path)
value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path)
value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path) && @response.redirected_to.is_a?(Hash)
end
value['controller'] = value['controller'][1..-1] if value['controller'].first == '/' # strip leading hash
end

View File

@@ -114,6 +114,9 @@ module ActionController
#
# # Tests a route, providing a defaults hash
# assert_routing 'controller/action/9', {:id => "9", :item => "square"}, {:controller => "controller", :action => "action"}, {}, {:item => "square"}
#
# # Tests a route with a HTTP method
# assert_routing({ :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" })
def assert_routing(path, options, defaults={}, extras={}, message=nil)
assert_recognizes(options, path, extras, message)
@@ -122,7 +125,7 @@ module ActionController
options[:controller] = "/#{controller}"
end
assert_generates(path, options, defaults, extras, message)
assert_generates(path.is_a?(Hash) ? path[:path] : path, options, defaults, extras, message)
end
private
@@ -140,4 +143,4 @@ module ActionController
end
end
end
end
end

View File

@@ -850,8 +850,8 @@ module ActionController #:nodoc:
response.headers["Location"] = url_for(location)
end
if text = options[:text]
render_for_text(text, options[:status])
if options.has_key?(:text)
render_for_text(options[:text], options[:status])
else
if file = options[:file]
@@ -1029,7 +1029,8 @@ module ActionController #:nodoc:
# RedirectBackError will be raised. You may specify some fallback
# behavior for this case by rescuing RedirectBackError.
def redirect_to(options = {}, response_status = {}) #:doc:
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
if options.is_a?(Hash) && options[:status]
status = options.delete(:status)
elsif response_status[:status]

View File

@@ -89,7 +89,7 @@ class CGI #:nodoc:
cookies = Hash.new([])
if raw_cookie
raw_cookie.split(/[;,]\s?/).each do |pairs|
raw_cookie.split(/;\s?/).each do |pairs|
name, values = pairs.split('=',2)
next unless name and values
name = CGI::unescape(name)

View File

@@ -583,10 +583,12 @@ module ActionController #:nodoc:
when filter.respond_to?(:call)
if filter.is_a?(Method)
MethodFilter
elsif filter.arity == 1
ProcFilter
else
ProcWithCallFilter
case filter.arity
when 1; ProcFilter
when 2; ProcWithCallFilter
else raise ArgumentError, 'Filter blocks must take one or two arguments.'
end
end
when filter.respond_to?(:filter)
ClassFilter

View File

@@ -145,7 +145,10 @@ module Mime
end
def ==(mime_type)
(@synonyms + [ self ]).any? { |synonym| synonym.to_s == mime_type.to_s } if mime_type
return false unless mime_type
(@synonyms + [ self ]).any? do |synonym|
synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
end
end
private

View File

@@ -1,8 +1,75 @@
module ActionController
# Polymorphic URL helpers are methods for smart resolution to a named route call when
# given an ActiveRecord model instance. They are to be used in combination with
# ActionController::Resources.
#
# These methods are useful when you want to generate correct URL or path to a RESTful
# resource without having to know the exact type of the record in question.
#
# Nested resources and/or namespaces are also supported, as illustrated in the example:
#
# polymorphic_url([:admin, @article, @comment])
# #-> results in:
# admin_article_comment_url(@article, @comment)
#
# == Usage within the framework
#
# Polymorphic URL helpers are used in a number of places throughout the Rails framework:
#
# * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
# <tt>url_for(@article)</tt>;
# * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
# <tt>form_for(@article)</tt> without having to specify :url parameter for the form
# action;
# * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
# <tt>redirect_to(post)</tt> in your controllers;
# * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
# for feed entries.
#
# == Prefixed polymorphic helpers
#
# In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
# number of prefixed helpers are available as a shorthand to <tt>:action => "..."</tt>
# in options. Those are:
#
# * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
# * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
# * <tt>formatted_polymorphic_url</tt>, <tt>formatted_polymorphic_path</tt>
#
# Example usage:
#
# edit_polymorphic_path(@post)
# #=> /posts/1/edit
#
# formatted_polymorphic_path([@post, :pdf])
# #=> /posts/1.pdf
module PolymorphicRoutes
# Constructs a call to a named RESTful route for the given record and returns the
# resulting URL string. For example:
#
# polymorphic_url(post)
# # calls post_url(post) #=> "http://example.com/posts/1"
#
# ==== Options
# * <tt>:action</tt> -- specifies the action prefix for the named route:
# <tt>:new</tt>, <tt>:edit</tt> or <tt>:formatted</tt>. Default is no prefix.
# * <tt>:routing_type</tt> -- <tt>:path</tt> or <tt>:url</tt> (default <tt>:url</tt>).
#
# ==== Examples
#
# # an Article record
# polymorphic_url(record) #-> article_url(record)
#
# # a Comment record
# polymorphic_url(record) #-> comment_url(record)
#
# # it recognizes new records and maps to the collection
# record = Comment.new
# polymorphic_url(record) #-> comments_url()
#
def polymorphic_url(record_or_hash_or_array, options = {})
record = extract_record(record_or_hash_or_array)
record = extract_record(record_or_hash_or_array)
format = (options[:action].to_s == "formatted" and record_or_hash_or_array.pop)
namespace = extract_namespace(record_or_hash_or_array)
args = case record_or_hash_or_array
@@ -11,9 +78,11 @@ module ActionController
else [ record_or_hash_or_array ]
end
args << format if format
inflection =
case
when options[:action] == "new"
when options[:action].to_s == "new"
args.pop
:singular
when record.respond_to?(:new_record?) && record.new_record?
@@ -27,8 +96,11 @@ module ActionController
send!(named_route, *args)
end
def polymorphic_path(record_or_hash_or_array)
polymorphic_url(record_or_hash_or_array, :routing_type => :path)
# Returns the path component of a URL for the given record. It uses
# <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>.
def polymorphic_path(record_or_hash_or_array, options = {})
options[:routing_type] = :path
polymorphic_url(record_or_hash_or_array, options)
end
%w(edit new formatted).each do |action|
@@ -43,26 +115,29 @@ module ActionController
EOT
end
private
def action_prefix(options)
options[:action] ? "#{options[:action]}_" : ""
end
def routing_type(options)
"#{options[:routing_type] || "url"}"
options[:routing_type] || :url
end
def build_named_route_call(records, namespace, inflection, options = {})
records = Array.new([extract_record(records)]) unless records.is_a?(Array)
base_segment = "#{RecordIdentifier.send!("#{inflection}_class_name", records.pop)}_"
method_root = records.reverse.inject(base_segment) do |string, name|
segment = "#{RecordIdentifier.send!("singular_class_name", name)}_"
segment << string
unless records.is_a?(Array)
record = extract_record(records)
route = ''
else
record = records.pop
route = records.inject("") do |string, parent|
string << "#{RecordIdentifier.send!("singular_class_name", parent)}_"
end
end
action_prefix(options) + namespace + method_root + routing_type(options)
route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_"
action_prefix(options) + namespace + route + routing_type(options).to_s
end
def extract_record(record_or_hash_or_array)
@@ -78,7 +153,7 @@ module ActionController
if record_or_hash_or_array.is_a?(Array)
record_or_hash_or_array.delete_if do |record_or_namespace|
if record_or_namespace.is_a?(String) || record_or_namespace.is_a?(Symbol)
namespace << "#{record_or_namespace.to_s}_"
namespace << "#{record_or_namespace}_"
end
end
end

View File

@@ -53,15 +53,15 @@ module ActionController
[ prefix, singular_class_name(record_or_class) ].compact * '_'
end
# The DOM class convention is to use the singular form of an object or class with the id following an underscore.
# The DOM id convention is to use the singular form of an object or class with the id following an underscore.
# If no id is found, prefix with "new_" instead. Examples:
#
# dom_class(Post.new(:id => 45)) # => "post_45"
# dom_class(Post.new) # => "new_post"
# dom_id(Post.new(:id => 45)) # => "post_45"
# dom_id(Post.new) # => "new_post"
#
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
#
# dom_class(Post.new(:id => 45), :edit) # => "edit_post_45"
# dom_id(Post.new(:id => 45), :edit) # => "edit_post_45"
def dom_id(record, prefix = nil)
prefix ||= 'new' unless record.id
[ prefix, singular_class_name(record), record.id ].compact * '_'

View File

@@ -111,7 +111,7 @@ module ActionController
# end
def format=(extension)
parameters[:format] = extension.to_s
format
@format = Mime::Type.lookup_by_extension(parameters[:format])
end
# Returns true if the request's "X-Requested-With" header contains
@@ -122,26 +122,41 @@ module ActionController
end
alias xhr? :xml_http_request?
# Which IP addresses are "trusted proxies" that can be stripped from
# the right-hand-side of X-Forwarded-For
TRUSTED_PROXIES = /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
# Determine originating IP address. REMOTE_ADDR is the standard
# but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or
# HTTP_X_FORWARDED_FOR are set by proxies so check for these before
# falling back to REMOTE_ADDR. HTTP_X_FORWARDED_FOR may be a comma-
# delimited list in the case of multiple chained proxies; the first is
# the originating IP.
#
# Security note: do not use if IP spoofing is a concern for your
# application. Since remote_ip checks HTTP headers for addresses forwarded
# by proxies, the client may send any IP. remote_addr can't be spoofed but
# also doesn't work behind a proxy, since it's always the proxy's IP.
# HTTP_X_FORWARDED_FOR are set by proxies so check for these if
# REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma-
# delimited list in the case of multiple chained proxies; the last
# address which is not trusted is the originating IP.
def remote_ip
return @env['HTTP_CLIENT_IP'] if @env.include? 'HTTP_CLIENT_IP'
if TRUSTED_PROXIES !~ @env['REMOTE_ADDR']
return @env['REMOTE_ADDR']
end
if @env.include? 'HTTP_CLIENT_IP'
if @env.include? 'HTTP_X_FORWARDED_FOR'
# We don't know which came from the proxy, and which from the user
raise ActionControllerError.new(<<EOM)
IP spoofing attack?!
HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}
HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
EOM
end
return @env['HTTP_CLIENT_IP']
end
if @env.include? 'HTTP_X_FORWARDED_FOR' then
remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip|
ip.strip =~ /^unknown$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',')
while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip
remote_ips.pop
end
return remote_ips.first.strip unless remote_ips.empty?
return remote_ips.last.strip
end
@env['REMOTE_ADDR']

View File

@@ -13,33 +13,46 @@ module ActionController #:nodoc:
base.extend(ClassMethods)
end
# Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a
# forged link from another site, is done by embedding a token based on the session (which an attacker wouldn't know) in all
# forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
# HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
# scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway.
#
# This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
# ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in
# production by editing public/422.html. A call to this method in ApplicationController is generated by default in post-Rails 2.0
# applications.
#
# The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form manually (without the
# use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to include a hidden field named like that and
# set its value to what is returned by <tt>form_authenticity_token</tt>. Same applies to manually constructed Ajax requests. To
# make the token available through a global variable to scripts on a certain page, you could add something like this to a view:
#
# <%= javascript_tag "window._token = '#{form_authenticity_token}'" %>
#
# Request forgery protection is disabled by default in test environment. If you are upgrading from Rails 1.x, add this to
# config/environments/test.rb:
#
# # Disable request forgery protection in test environment
# config.action_controller.allow_forgery_protection = false
#
# == Learn more about CSRF (Cross-Site Request Forgery) attacks
#
# Here are some resources:
# * http://isc.sans.org/diary.html?storyid=1750
# * http://en.wikipedia.org/wiki/Cross-site_request_forgery
#
# Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
# There are a few guidelines you should follow:
#
# * Keep your GET requests safe and idempotent. More reading material:
# * http://www.xml.com/pub/a/2002/04/24/deviant.html
# * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
# * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session"
#
module ClassMethods
# Protect a controller's actions from CSRF attacks by ensuring that all forms are coming from the current web application, not
# a forged link from another site. This is done by embedding a token based on the session (which an attacker wouldn't know) in
# all forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
# HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
# scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway.
#
# You turn this on with the #protect_from_forgery method, which will perform the check and raise
# an ActionController::InvalidAuthenticityToken if the token doesn't match what was expected. And it will add
# a _authenticity_token parameter to all forms that are automatically generated by Rails. You can customize the error message
# given through public/422.html.
#
# Learn more about CSRF (Cross-Site Request Forgery) attacks:
#
# * http://isc.sans.org/diary.html?storyid=1750
# * http://en.wikipedia.org/wiki/Cross-site_request_forgery
#
# Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
# There are a few guidelines you should follow:
#
# * Keep your GET requests safe and idempotent. More reading material:
# * http://www.xml.com/pub/a/2002/04/24/deviant.html
# * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
# * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session"
#
# If you need to construct a request yourself, but still want to take advantage of forgery protection, you can grab the
# authenticity_token using the form_authenticity_token helper method and make it part of the parameters yourself.
# Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
#
# Example:
#
@@ -54,16 +67,10 @@ module ActionController #:nodoc:
# skip_before_filter :verify_authenticity_token
# end
#
# If you are upgrading from Rails 1.x, disable forgery protection to
# simplify your tests. Add this to config/environments/test.rb:
#
# # Disable request forgery protection in test environment
# config.action_controller.allow_forgery_protection = false
#
# Valid Options:
#
# * <tt>:only/:except</tt> - passed to the before_filter call. Set which actions are verified.
# * <tt>:secret</tt> - Custom salt used to generate the form_authenticity_token.
# * <tt>:only/:except</tt> - passed to the <tt>before_filter</tt> call. Set which actions are verified.
# * <tt>:secret</tt> - Custom salt used to generate the <tt>form_authenticity_token</tt>.
# Leave this off if you are using the cookie session store.
# * <tt>:digest</tt> - Message digest used for hashing. Defaults to 'SHA1'
def protect_from_forgery(options = {})

View File

@@ -306,8 +306,8 @@ module ActionController
gsub(%r{(.)[\\/]$}, '\1') # drop final / or \ if path ends with it
# eliminate .. paths where possible
re = %r{\w+[/\\]\.\.[/\\]}
path.gsub!(%r{\w+[/\\]\.\.[/\\]}, "") while path.match(re)
re = %r{[^/\\]+[/\\]\.\.[/\\]}
path.gsub!(re, "") while path.match(re)
path
end
@@ -1216,7 +1216,7 @@ module ActionController
opts = if args.empty? || Hash === args.first
args.first || {}
else
options = args.last.is_a?(Hash) ? args.pop : {}
options = args.extract_options!
args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)|
h[k] = v
h

View File

@@ -10,17 +10,17 @@
<p><code>RAILS_ROOT: <%= defined?(RAILS_ROOT) ? RAILS_ROOT : "unset" %></code></p>
<div id="traces">
<% names.each do |name| -%>
<% names.each do |name| %>
<%
show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';"
hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"}
%>
<a href="#" onclick="<%= hide %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
<% end -%>
<% end %>
<% traces.each do |name, trace| -%>
<% traces.each do |name, trace| %>
<div id="<%= name.gsub /\s/, '-' %>" style="display: <%= name == "Application Trace" ? 'block' : 'none' %>;">
<pre><code><%= trace.join "\n" %></code></pre>
</div>
<% end -%>
</div>
<% end %>
</div>

View File

@@ -3,9 +3,15 @@ require 'active_support/test_case'
module ActionController
class NonInferrableControllerError < ActionControllerError
def initialize(name)
@name = name
super "Unable to determine the controller to test from #{name}. " +
"You'll need to specify it using 'tests YourController' in your " +
"test case definition"
"test case definition. This could mean that #{inferred_controller_name} does not exist " +
"or it contains syntax errors"
end
def inferred_controller_name
@name.sub(/Test$/, '')
end
end
@@ -44,10 +50,24 @@ module ActionController
end
end
def setup
def setup_with_controller
@controller = self.class.controller_class.new
@request = TestRequest.new
@response = TestResponse.new
end
end
end
alias_method :setup, :setup_with_controller
def self.method_added(method)
if method.to_s == 'setup'
unless method_defined?(:setup_without_controller)
alias_method :setup_without_controller, :setup
define_method(:setup) do
setup_with_fixtures if respond_to?(:setup_with_fixtures)
setup_with_controller
setup_without_controller
end
end
end
end
end
end

View File

@@ -286,7 +286,7 @@ module ActionController #:nodoc:
def initialize(attributes = nil)
@session_id = ''
@attributes = attributes
@attributes = attributes.nil? ? nil : attributes.stringify_keys
@saved_attributes = nil
end
@@ -295,11 +295,11 @@ module ActionController #:nodoc:
end
def [](key)
data[key]
data[key.to_s]
end
def []=(key, value)
data[key] = value
data[key.to_s] = value
end
def update

View File

@@ -1,17 +1,17 @@
module ActionController
module ActionController
# Write URLs from arbitrary places in your codebase, such as your mailers.
#
#
# Example:
#
#
# class MyMailer
# include ActionController::UrlWriter
# default_url_options[:host] = 'www.basecamphq.com'
#
#
# def signup_url(token)
# url_for(:controller => 'signup', action => 'index', :token => token)
# end
# end
#
#
# In addition to providing +url_for+, named routes are also accessible after
# including UrlWriter.
module UrlWriter
@@ -19,64 +19,65 @@ module ActionController
# is provided.
mattr_accessor :default_url_options
self.default_url_options = {}
def self.included(base) #:nodoc:
ActionController::Routing::Routes.install_helpers base
ActionController::Routing::Routes.install_helpers(base)
base.mattr_accessor :default_url_options
base.default_url_options ||= default_url_options
end
# Generate a url based on the options provided, default_url_options and the
# Generate a url based on the options provided, default_url_options and the
# routes defined in routes.rb. The following options are supported:
#
#
# * <tt>:only_path</tt> If true, the relative url is returned. Defaults to false.
# * <tt>:protocol</tt> The protocol to connect to. Defaults to 'http'.
# * <tt>:host</tt> Specifies the host the link should be targetted at. If <tt>:only_path</tt> is false, this option must be
# provided either explicitly, or via default_url_options.
# * <tt>:host</tt> Specifies the host the link should be targetted at. If <tt>:only_path</tt> is false, this option must be
# provided either explicitly, or via default_url_options.
# * <tt>:port</tt> Optionally specify the port to connect to.
# * <tt>:anchor</tt> An anchor name to be appended to the path.
#
# * <tt>:skip_relative_url_root</tt> If true, the url is not constructed using the relative_url_root set in <tt>ActionController::AbstractRequest.relative_url_root</tt>.
#
# Any other key(:controller, :action, etc...) given to <tt>url_for</tt> is forwarded to the Routes module.
#
#
# Examples:
#
#
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
#
def url_for(options)
options = self.class.default_url_options.merge(options)
url = ''
unless options.delete :only_path
unless options.delete(:only_path)
url << (options.delete(:protocol) || 'http')
url << '://' unless url.match("://") #dont add separator if its already been specified in :protocol
url << '://' unless url.match("://")
raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
url << options.delete(:host)
url << ":#{options.delete(:port)}" if options.key?(:port)
else
# Delete the unused options to prevent their appearance in the query string
[:protocol, :host, :port].each { |k| options.delete k }
# Delete the unused options to prevent their appearance in the query string.
[:protocol, :host, :port, :skip_relative_url_root].each { |k| options.delete(k) }
end
anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options.key?(:anchor)
url << ActionController::AbstractRequest.relative_url_root.to_s unless options[:skip_relative_url_root]
anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor]
url << Routing::Routes.generate(options, {})
url << anchor if anchor
return url
end
url
end
end
# Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
class UrlRewriter #:nodoc:
RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :skip_relative_url_root]
def initialize(request, parameters)
@request, @parameters = request, parameters
end
def rewrite(options = {})
rewrite_url(options)
end
@@ -123,7 +124,7 @@ module ActionController
# Generates the query string, too
Routing::Routes.generate(options, @request.symbolized_path_parameters)
end
def rewrite_authentication(options)
if options[:user] && options[:password]
"#{CGI.escape(options.delete(:user))}:#{CGI.escape(options.delete(:password))}@"

View File

@@ -43,72 +43,88 @@ module ActionController #:nodoc:
# the user is redirected to a different action. The +options+ parameter
# is a hash consisting of the following key/value pairs:
#
# * <tt>:params</tt> - a single key or an array of keys that must
# be in the <tt>params</tt> hash in order for the action(s) to be safely
# called.
# * <tt>:session</tt> - a single key or an array of keys that must
# be in the <tt>session</tt> in order for the action(s) to be safely called.
# * <tt>:flash</tt> - a single key or an array of keys that must
# be in the flash in order for the action(s) to be safely called.
# * <tt>:method</tt> - a single key or an array of keys--any one of which
# must match the current request method in order for the action(s) to
# be safely called. (The key should be a symbol: <tt>:get</tt> or
# <tt>:post</tt>, for example.)
# * <tt>:xhr</tt> - true/false option to ensure that the request is coming
# from an Ajax call or not.
# * <tt>:add_flash</tt> - a hash of name/value pairs that should be merged
# into the session's flash if the prerequisites cannot be satisfied.
# * <tt>:add_headers</tt> - a hash of name/value pairs that should be
# merged into the response's headers hash if the prerequisites cannot
# be satisfied.
# * <tt>:redirect_to</tt> - the redirection parameters to be used when
# redirecting if the prerequisites cannot be satisfied. You can
# redirect either to named route or to the action in some controller.
# * <tt>:render</tt> - the render parameters to be used when
# the prerequisites cannot be satisfied.
# * <tt>:only</tt> - only apply this verification to the actions specified
# in the associated array (may also be a single value).
# * <tt>:except</tt> - do not apply this verification to the actions
# specified in the associated array (may also be a single value).
# <tt>:params</tt>::
# a single key or an array of keys that must be in the <tt>params</tt>
# hash in order for the action(s) to be safely called.
# <tt>:session</tt>::
# a single key or an array of keys that must be in the <tt>session</tt>
# in order for the action(s) to be safely called.
# <tt>:flash</tt>::
# a single key or an array of keys that must be in the flash in order
# for the action(s) to be safely called.
# <tt>:method</tt>::
# a single key or an array of keys--any one of which must match the
# current request method in order for the action(s) to be safely called.
# (The key should be a symbol: <tt>:get</tt> or <tt>:post</tt>, for
# example.)
# <tt>:xhr</tt>::
# true/false option to ensure that the request is coming from an Ajax
# call or not.
# <tt>:add_flash</tt>::
# a hash of name/value pairs that should be merged into the session's
# flash if the prerequisites cannot be satisfied.
# <tt>:add_headers</tt>::
# a hash of name/value pairs that should be merged into the response's
# headers hash if the prerequisites cannot be satisfied.
# <tt>:redirect_to</tt>::
# the redirection parameters to be used when redirecting if the
# prerequisites cannot be satisfied. You can redirect either to named
# route or to the action in some controller.
# <tt>:render</tt>::
# the render parameters to be used when the prerequisites cannot be satisfied.
# <tt>:only</tt>::
# only apply this verification to the actions specified in the associated
# array (may also be a single value).
# <tt>:except</tt>::
# do not apply this verification to the actions specified in the associated
# array (may also be a single value).
def verify(options={})
filter_opts = { :only => options[:only], :except => options[:except] }
before_filter(filter_opts) do |c|
before_filter :only => options[:only], :except => options[:except] do |c|
c.send! :verify_action, options
end
end
end
def verify_action(options) #:nodoc:
prereqs_invalid =
[*options[:params] ].find { |v| params[v].nil? } ||
[*options[:session]].find { |v| session[v].nil? } ||
[*options[:flash] ].find { |v| flash[v].nil? }
if !prereqs_invalid && options[:method]
prereqs_invalid ||=
[*options[:method]].all? { |v| request.method != v.to_sym }
end
prereqs_invalid ||= (request.xhr? != options[:xhr]) unless options[:xhr].nil?
if prereqs_invalid
flash.update(options[:add_flash]) if options[:add_flash]
response.headers.update(options[:add_headers]) if options[:add_headers]
private
unless performed?
case
when options[:render]
render(options[:render])
when options[:redirect_to]
options[:redirect_to] = self.send!(options[:redirect_to]) if options[:redirect_to].is_a?(Symbol)
redirect_to(options[:redirect_to])
else
head(:bad_request)
end
end
def verify_action(options) #:nodoc:
if prereqs_invalid?(options)
flash.update(options[:add_flash]) if options[:add_flash]
response.headers.update(options[:add_headers]) if options[:add_headers]
apply_remaining_actions(options) unless performed?
end
end
def prereqs_invalid?(options) # :nodoc:
verify_presence_of_keys_in_hash_flash_or_params(options) ||
verify_method(options) ||
verify_request_xhr_status(options)
end
def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc:
[*options[:params] ].find { |v| params[v].nil? } ||
[*options[:session]].find { |v| session[v].nil? } ||
[*options[:flash] ].find { |v| flash[v].nil? }
end
def verify_method(options) # :nodoc:
[*options[:method]].all? { |v| request.method != v.to_sym } if options[:method]
end
def verify_request_xhr_status(options) # :nodoc:
request.xhr? != options[:xhr] unless options[:xhr].nil?
end
def apply_redirect_to(redirect_to_option) # :nodoc:
redirect_to_option.is_a?(Symbol) ? self.send!(redirect_to_option) : redirect_to_option
end
def apply_remaining_actions(options) # :nodoc:
case
when options[:render] ; render(options[:render])
when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to]))
else head(:bad_request)
end
end
private :verify_action
end
end

View File

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

View File

@@ -157,6 +157,8 @@ module ActionView #:nodoc:
attr_reader :logger, :response, :headers
attr_internal :cookies, :flash, :headers, :params, :request, :response, :session
delegate :logger, :action_name, :to => :controller
attr_writer :template_format
# Specify trim mode for the ERB compiler. Defaults to '-'.
@@ -338,11 +340,13 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
path, partial_name = partial_pieces(options.delete(:layout))
if block_given?
@content_for_layout = capture(&block)
concat(render(options.merge(:partial => "#{path}/#{partial_name}")), block.binding)
wrap_content_for_layout capture(&block) do
concat(render(options.merge(:partial => "#{path}/#{partial_name}")), block.binding)
end
else
@content_for_layout = render(options)
render(options.merge(:partial => "#{path}/#{partial_name}"))
wrap_content_for_layout render(options) do
render(options.merge(:partial => "#{path}/#{partial_name}"))
end
end
elsif options[:file]
render_file(options[:file], options[:use_full_path], options[:locals])
@@ -441,6 +445,12 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
end
private
def wrap_content_for_layout(content)
original_content_for_layout = @content_for_layout
@content_for_layout = content
returning(yield) { @content_for_layout = original_content_for_layout }
end
def find_full_template_path(template_path, extension)
file_name = "#{template_path}.#{extension}"
base_path = find_base_path_for(file_name)
@@ -516,10 +526,18 @@ If you are rendering a subtemplate, you must now use controller-like partial syn
def template_handler_is_compilable?(handler)
handler.new(self).respond_to?(:compile)
end
# Assigns instance variables from the controller to the view.
def assign_variables_from_controller
@assigns.each { |key, value| instance_variable_set("@#{key}", value) }
@assigns.each do |key, value|
if ['logger'].include?(key)
instance_variable_set("@#{key}", ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, key.to_sym))
elsif ['action_name'].include?(key)
instance_variable_set("@#{key}", ActiveSupport::Deprecation::DeprecatedInstanceVariable.new(value, key))
else
instance_variable_set("@#{key}", value)
end
end
end

View File

@@ -217,7 +217,7 @@ module ActionView
# <script type="text/javascript" src="/javascripts/cart.js"></script>
# <script type="text/javascript" src="/javascripts/checkout.js"></script>
#
# javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when ActionController::Base.perform_caching is false =>
# javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when ActionController::Base.perform_caching is true =>
# <script type="text/javascript" src="/javascripts/shop.js"></script>
def javascript_include_tag(*sources)
options = sources.extract_options!.stringify_keys
@@ -435,9 +435,11 @@ module ActionView
else
source = "/#{dir}/#{source}" unless source[0] == ?/
if has_request
source = "#{@controller.request.relative_url_root}#{source}"
unless source =~ %r{^#{@controller.request.relative_url_root}/}
source = "#{@controller.request.relative_url_root}#{source}"
end
end
rewrite_asset_path!(source)
source = rewrite_asset_path(source)
if include_host
host = compute_asset_host(source)
@@ -484,11 +486,15 @@ module ActionView
end
end
# Break out the asset path rewrite so you wish to put the asset id
# Break out the asset path rewrite in case plugins wish to put the asset id
# someplace other than the query string.
def rewrite_asset_path!(source)
def rewrite_asset_path(source)
asset_id = rails_asset_id(source)
source << "?#{asset_id}" if !asset_id.blank?
if asset_id.blank?
source
else
source + "?#{asset_id}"
end
end
def javascript_src_tag(source, options)

View File

@@ -26,7 +26,7 @@ module ActionView
# end
#
# app/views/posts/index.atom.builder:
# atom_feed do |feed|
# atom_feed(:tag_uri => "2008") do |feed|
# feed.title("My great blog!")
# feed.updated((@posts.first.created_at))
#
@@ -44,31 +44,35 @@ module ActionView
#
# The options are for atom_feed are:
#
# * <tt>:schema_date</tt>: Required. The date at which the tag scheme for the feed was first used. A good default is the year you created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information.
# * <tt>:language</tt>: Defaults to "en-US".
# * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
# * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
#
# atom_feed yields a AtomFeedBuilder instance.
# atom_feed yields an AtomFeedBuilder instance.
def atom_feed(options = {}, &block)
if options[:schema_date].blank?
logger.warn("You must provide the :schema_date option to atom_feed for your feed to be valid. A good default is the year you first created this feed.") unless logger.nil?
else
options[:schema_date] = options[:schema_date].strftime("%Y-%m-%d") if options[:schema_date].respond_to?(:strftime)
end
xml = options[:xml] || eval("xml", block.binding)
xml.instruct!
xml.feed "xml:lang" => options[:language] || "en-US", "xmlns" => 'http://www.w3.org/2005/Atom' do
xml.id("tag:#{request.host}:#{request.request_uri.split(".")[0].gsub("/", "")}")
xml.id("tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}")
xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
if options[:url]
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
end
yield AtomFeedBuilder.new(xml, self)
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
yield AtomFeedBuilder.new(xml, self, options)
end
end
class AtomFeedBuilder
def initialize(xml, view)
@xml, @view = xml, view
def initialize(xml, view, feed_options = {})
@xml, @view, @feed_options = xml, view, feed_options
end
# Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
@@ -80,12 +84,12 @@ module ActionView
#
# Options:
#
# * <tt>:updated</tt>: Time of update. Defaults to the created_at attribute on the record if one such exists.
# * <tt>:published</tt>: Time first published. Defaults to the updated_at attribute on the record if one such exists.
# * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
# * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
# * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
def entry(record, options = {})
@xml.entry do
@xml.id("tag:#{@view.request.host_with_port}:#{record.class}#{record.id}")
@xml.id("tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
if options[:published] || (record.respond_to?(:created_at) && record.created_at)
@xml.published((options[:published] || record.created_at).xmlschema)
@@ -102,10 +106,10 @@ module ActionView
end
private
def method_missing(method, *arguments)
@xml.__send__(method, *arguments)
def method_missing(method, *arguments, &block)
@xml.__send__(method, *arguments, &block)
end
end
end
end
end
end

View File

@@ -155,8 +155,9 @@ module ActionView
#
# In many cases you will want to wrap the above in another helper, so you could do something like the following:
#
# def labelled_form_for(name, object, options, &proc)
# form_for(name, object, options.merge(:builder => LabellingFormBuiler), &proc)
# def labelled_form_for(record_or_name_or_array, *args, &proc)
# options = args.extract_options!
# form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &proc)
# end
#
# If you don't need to attach a form to a model instance, then check out FormTagHelper#form_tag.

View File

@@ -114,6 +114,24 @@ module ActionView
tag :input, { "type" => "text", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys)
end
# Creates a label field
#
# ==== Options
# * Creates standard HTML attributes for the tag.
#
# ==== Examples
# label_tag 'name'
# # => <label for="name">Name</label>
#
# label_tag 'name', 'Your name'
# # => <label for="name">Your Name</label>
#
# label_tag 'name', nil, :class => 'small_label'
# # => <label for="name" class="small_label">Name</label>
def label_tag(name, text = nil, options = {})
content_tag :label, text || name.humanize, { "for" => name }.update(options.stringify_keys)
end
# Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
# data that should be hidden from the user.
#

View File

@@ -170,7 +170,7 @@ module ActionView
when size < 1.gigabyte; "%.#{precision}f MB" % (size / 1.0.megabyte)
when size < 1.terabyte; "%.#{precision}f GB" % (size / 1.0.gigabyte)
else "%.#{precision}f TB" % (size / 1.0.terabyte)
end.sub(/([0-9])\.?0+ /, '\1 ' )
end.sub(/([0-9]\.\d*?)0+ /, '\1 ' ).sub(/\. /,' ')
rescue
nil
end

View File

@@ -1019,7 +1019,7 @@ module ActionView
js_options['parameters'] = options[:with]
end
if protect_against_forgery?
if protect_against_forgery? && !options[:form]
if js_options['parameters']
js_options['parameters'] << " + '&"
else

View File

@@ -436,7 +436,7 @@ module ActionView
[-\w]+ # subdomain or domain
(?:\.[-\w]+)* # remaining subdomains or domain
(?::\d+)? # port
(?:/(?:(?:[~\w\+@%-]|(?:[,.;:][^\s$]))+)?)* # path
(?:/(?:(?:[~\w\+@%=-]|(?:[,.;:][^\s$]))+)?)* # path
(?:\?[\w\+@%&=.;-]+)? # query string
(?:\#[\w\-]*)? # trailing anchor
)

View File

@@ -10,6 +10,7 @@ module ActionView
@base_path, @assigns, @source, @original_exception =
base_path, assigns.dup, source, original_exception
@file_path = file_path
@backtrace = compute_backtrace
end
def message
@@ -72,14 +73,20 @@ module ActionView
"#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
end
# don't do anything nontrivial here. Any raised exception from here becomes fatal
# (and can't be rescued).
def backtrace
[
"#{source_location.capitalize}\n\n#{source_extract(4)}\n " +
clean_backtrace.join("\n ")
]
@backtrace
end
private
def compute_backtrace
[
"#{source_location.capitalize}\n\n#{source_extract(4)}\n " +
clean_backtrace.join("\n ")
]
end
def strip_base_path(path)
stripped_path = File.expand_path(path).gsub(@base_path, "")
stripped_path.gsub!(/^#{Regexp.escape File.expand_path(RAILS_ROOT)}/, '') if defined?(RAILS_ROOT)

View File

@@ -0,0 +1,24 @@
require File.dirname(__FILE__) + '/../active_record_unit'
require "action_controller/test_case"
class ActionController::TestCase
self.fixture_path = File.dirname(__FILE__) + '/../fixtures'
self.use_transactional_fixtures = false
end
class DeveloperController < ActionController::Base
end
class DeveloperControllerTest < ActionController::TestCase
fixtures :developers
def setup
@david = developers(:david)
end
def test_should_have_loaded_fixtures
assert_kind_of(Developer, @david)
assert_kind_of(Developer, developers(:jamis))
assert_equal(@developers.size, Developer.count)
end
end

View File

@@ -124,6 +124,18 @@ class ActionPackAssertionsController < ActionController::Base
def rescue_action(e) raise; end
end
# Used to test that assert_response includes the exception message
# in the failure message when an action raises and assert_response
# is expecting something other than an error.
class AssertResponseWithUnexpectedErrorController < ActionController::Base
def index
raise 'FAIL'
end
end
class UserController < ActionController::Base
end
module Admin
class InnerModuleController < ActionController::Base
def index
@@ -161,7 +173,7 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
# let's get this party started
def setup
ActionController::Routing::Routes.reload
ActionController::Routing.use_controllers!(%w(action_pack_assertions admin/inner_module content admin/user))
ActionController::Routing.use_controllers!(%w(action_pack_assertions admin/inner_module user content admin/user))
@controller = ActionPackAssertionsController.new
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
end
@@ -255,7 +267,7 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
assert_redirected_to admin_inner_module_path
end
end
def test_assert_redirected_to_top_level_named_route_from_nested_controller
with_routing do |set|
set.draw do |map|
@@ -269,6 +281,20 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
end
end
def test_assert_redirected_to_top_level_named_route_with_same_controller_name_in_both_namespaces
with_routing do |set|
set.draw do |map|
# this controller exists in the admin namespace as well which is the only difference from previous test
map.top_level '/user/:id', :controller => 'user', :action => 'index'
map.connect ':controller/:action/:id'
end
@controller = Admin::InnerModuleController.new
process :redirect_to_top_level_named_route
assert_redirected_to "/user/foo"
end
end
# -- standard request/response object testing --------------------------------
# make sure that the template objects exist
@@ -465,6 +491,15 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase
rescue Test::Unit::AssertionFailedError => e
end
end
def test_assert_response_uses_exception_message
@controller = AssertResponseWithUnexpectedErrorController.new
get :index
assert_response :success
flunk 'Expected non-success response'
rescue Test::Unit::AssertionFailedError => e
assert e.message.include?('FAIL')
end
end
class ActionPackHeaderTest < Test::Unit::TestCase

View File

@@ -87,7 +87,10 @@ class ControllerInstanceTests < Test::Unit::TestCase
# Mocha adds some public instance methods to Object that would be
# considered actions, so explicitly hide_action them.
def hide_mocha_methods_from_controller(controller)
mocha_methods = [:expects, :metaclass, :mocha, :mocha_inspect, :reset_mocha, :stubba_object, :stubba_method, :stubs, :verify, :__metaclass__, :__is_a__]
mocha_methods = [
:expects, :mocha, :mocha_inspect, :reset_mocha, :stubba_object,
:stubba_method, :stubs, :verify, :__metaclass__, :__is_a__, :to_matcher,
]
controller.class.send!(:hide_action, *mocha_methods)
end
end

View File

@@ -4,8 +4,9 @@ require 'action_controller/cgi_process'
class BaseCgiTest < Test::Unit::TestCase
def setup
@request_hash = {"HTTP_MAX_FORWARDS"=>"10", "SERVER_NAME"=>"glu.ttono.us:8007", "FCGI_ROLE"=>"RESPONDER", "HTTP_X_FORWARDED_HOST"=>"glu.ttono.us", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1", "PATH_INFO"=>"", "HTTP_ACCEPT_LANGUAGE"=>"en", "HTTP_HOST"=>"glu.ttono.us:8007", "SERVER_PROTOCOL"=>"HTTP/1.1", "REDIRECT_URI"=>"/dispatch.fcgi", "SCRIPT_NAME"=>"/dispatch.fcgi", "SERVER_ADDR"=>"207.7.108.53", "REMOTE_ADDR"=>"207.7.108.53", "SERVER_SOFTWARE"=>"lighttpd/1.4.5", "HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", "HTTP_X_FORWARDED_SERVER"=>"glu.ttono.us", "REQUEST_URI"=>"/admin", "DOCUMENT_ROOT"=>"/home/kevinc/sites/typo/public", "SERVER_PORT"=>"8007", "QUERY_STRING"=>"", "REMOTE_PORT"=>"63137", "GATEWAY_INTERFACE"=>"CGI/1.1", "HTTP_X_FORWARDED_FOR"=>"65.88.180.234", "HTTP_ACCEPT"=>"*/*", "SCRIPT_FILENAME"=>"/home/kevinc/sites/typo/public/dispatch.fcgi", "REDIRECT_STATUS"=>"200", "REQUEST_METHOD"=>"GET"}
# cookie as returned by some Nokia phone browsers (no space after semicolon separator)
@alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2;is_admin=yes"}
# some Nokia phone browsers omit the space after the semicolon separator.
# some developers have grown accustomed to using comma in cookie values.
@alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"}
@fake_cgi = Struct.new(:env_table).new(@request_hash)
@request = ActionController::CgiRequest.new(@fake_cgi)
end
@@ -76,7 +77,7 @@ class CgiRequestTest < BaseCgiTest
assert_equal ["yes"], cookies["is_admin"], cookies.inspect
alt_cookies = CGI::Cookie::parse(@alt_cookie_fmt_request_hash["HTTP_COOKIE"]);
assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect
assert_equal ["c84ace847,96670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect
assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect
end
end

View File

@@ -696,10 +696,6 @@ class ControllerWithProcFilter < PostsController
end
end
class ControllerWithWrongFilterType < PostsController
around_filter lambda { yield }, :only => :no_raise
end
class ControllerWithNestedFilters < ControllerWithSymbolAsFilter
around_filter :raise_before, :raise_after, :without_exception, :only => :raises_both
end
@@ -746,14 +742,15 @@ class YieldingAroundFiltersTest < Test::Unit::TestCase
assert_equal 1, ControllerWithFilterClass.filter_chain.size
assert_equal 1, ControllerWithFilterInstance.filter_chain.size
assert_equal 3, ControllerWithSymbolAsFilter.filter_chain.size
assert_equal 1, ControllerWithWrongFilterType.filter_chain.size
assert_equal 6, ControllerWithNestedFilters.filter_chain.size
assert_equal 4, ControllerWithAllTypesOfFilters.filter_chain.size
end
def test_wrong_filter_type
assert_raise(ActionController::ActionControllerError) do
test_process(ControllerWithWrongFilterType,'no_raise')
assert_raise ArgumentError do
Class.new PostsController do
around_filter lambda { yield }
end
end
end

View File

@@ -204,9 +204,9 @@ class SanitizerTest < Test::Unit::TestCase
end
def test_should_sanitize_with_trailing_space
raw = "display:block; "
expected = "display: block;"
assert_equal expected, sanitize_css(raw)
raw = "display:block; "
expected = "display: block;"
assert_equal expected, sanitize_css(raw)
end
def test_should_sanitize_xul_style_attributes

View File

@@ -39,6 +39,11 @@ class MimeTypeTest < Test::Unit::TestCase
Mime.module_eval { remove_const :GIF if const_defined?(:GIF) }
end
def test_type_should_be_equal_to_symbol
assert_equal Mime::HTML, 'application/xhtml+xml'
assert_equal Mime::HTML, :html
end
def test_type_convenience_methods
types = [:html, :xml, :png, :pdf, :yaml, :url_encoded_form]
types.each do |type|

View File

@@ -361,10 +361,18 @@ class NewRenderTestController < ActionController::Base
render :action => "calling_partial_with_layout"
end
def render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout
render :action => "calling_partial_with_layout"
end
def render_using_layout_around_block
render :action => "using_layout_around_block"
end
def render_using_layout_around_block_in_main_layout_and_within_content_for_layout
render :action => "using_layout_around_block"
end
def rescue_action(e) raise end
private
@@ -387,6 +395,10 @@ class NewRenderTestController < ActionController::Base
"layouts/builder"
when "action_talk_to_layout", "layout_overriding_layout"
"layouts/talk_from_action"
when "render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout"
"layouts/partial_with_layout"
when "render_using_layout_around_block_in_main_layout_and_within_content_for_layout"
"layouts/block_with_layout"
end
end
end
@@ -824,9 +836,20 @@ EOS
get :render_call_to_partial_with_layout
assert_equal "Before (David)\nInside from partial (David)\nAfter", @response.body
end
def test_render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout
get :render_call_to_partial_with_layout_in_main_layout_and_within_content_for_layout
assert_equal "Before (Anthony)\nInside from partial (Anthony)\nAfter\nBefore (David)\nInside from partial (David)\nAfter\nBefore (Ramm)\nInside from partial (Ramm)\nAfter", @response.body
end
def test_using_layout_around_block
get :using_layout_around_block
get :render_using_layout_around_block
assert_equal "Before (David)\nInside from block\nAfter", @response.body
end
def test_using_layout_around_block_in_main_layout_and_within_content_for_layout
get :render_using_layout_around_block_in_main_layout_and_within_content_for_layout
assert_equal "Before (Anthony)\nInside from first block in layout\nAfter\nBefore (David)\nInside from block\nAfter\nBefore (Ramm)\nInside from second block in layout\nAfter\n", @response.body
end
end

View File

@@ -5,94 +5,125 @@ class Article
def save; @id = 1 end
def new_record?; @id.nil? end
def name
@id.nil? ? 'new post' : "post ##{@id}"
model = self.class.name.downcase
@id.nil? ? "new #{model}" : "#{model} ##{@id}"
end
end
class Comment
attr_reader :id
class Comment < Article
def post_id; 1 end
def save; @id = 1 end
def new_record?; @id.nil? end
def name
@id.nil? ? 'new comment' : "comment ##{@id}"
end
end
class Tag < Article
def comment_id; 1 end
end
# TODO: test nested models
class Comment::Nested < Comment; end
class Test::Unit::TestCase
protected
def articles_url
'http://www.example.com/articles'
end
alias_method :new_article_url, :articles_url
def article_url(article)
"http://www.example.com/articles/#{article.id}"
end
uses_mocha 'polymorphic URL helpers' do
class PolymorphicRoutesTest < Test::Unit::TestCase
def article_comments_url(article)
"http://www.example.com/articles/#{article.id}/comments"
end
include ActionController::PolymorphicRoutes
def setup
@article = Article.new
@comment = Comment.new
end
def article_comment_url(article, comment)
"http://www.example.com/articles/#{article.id}/comments/#{comment.id}"
end
def admin_articles_url
"http://www.example.com/admin/articles"
end
alias_method :new_admin_article_url, :admin_articles_url
def admin_article_url(article)
"http://www.example.com/admin/articles/#{article.id}"
end
def admin_article_comments_url(article)
"http://www.example.com/admin/articles/#{article.id}/comments"
end
def admin_article_comment_url(article, comment)
"http://www.example.com/admin/test/articles/#{article.id}/comments/#{comment.id}"
end
end
class PolymorphicRoutesTest < Test::Unit::TestCase
include ActionController::PolymorphicRoutes
def setup
@article = Article.new
@comment = Comment.new
end
def test_with_record
assert_equal(articles_url, polymorphic_url(@article, :action => 'new'))
assert_equal(articles_url, polymorphic_url(@article))
@article.save
assert_equal(article_url(@article), polymorphic_url(@article))
end
# TODO: Needs to be updated to correctly know about whether the object is in a hash or not
def xtest_with_hash
@article.save
assert_equal(article_url(@article), polymorphic_url(:id => @article))
end
def test_with_array
assert_equal(article_comments_url(@article), polymorphic_url([@article, @comment]))
@comment.save
assert_equal(article_comment_url(@article, @comment), polymorphic_url([@article, @comment]))
end
def test_with_array_and_namespace
assert_equal(admin_articles_url, polymorphic_url([:admin, @article], :action => 'new'))
assert_equal(admin_articles_url, polymorphic_url([:admin, @article]))
@article.save
assert_equal(admin_article_url(@article), polymorphic_url([:admin, @article]))
assert_equal(admin_article_comments_url(@article), polymorphic_url([:admin, @article, @comment]))
@comment.save
assert_equal(admin_article_comment_url(@article, @comment), polymorphic_url([:admin, @article, @comment]))
def test_with_record
@article.save
expects(:article_url).with(@article)
polymorphic_url(@article)
end
def test_with_new_record
expects(:articles_url).with()
@article.expects(:new_record?).returns(true)
polymorphic_url(@article)
end
def test_with_record_and_action
expects(:new_article_url).with()
@article.expects(:new_record?).never
polymorphic_url(@article, :action => 'new')
end
def test_url_helper_prefixed_with_new
expects(:new_article_url).with()
new_polymorphic_url(@article)
end
def test_url_helper_prefixed_with_edit
@article.save
expects(:edit_article_url).with(@article)
edit_polymorphic_url(@article)
end
def test_formatted_url_helper
expects(:formatted_article_url).with(@article, :pdf)
formatted_polymorphic_url([@article, :pdf])
end
# TODO: should this work?
def xtest_format_option
@article.save
expects(:article_url).with(@article, :format => :pdf)
polymorphic_url(@article, :format => :pdf)
end
def test_with_nested
@comment.save
expects(:article_comment_url).with(@article, @comment)
polymorphic_url([@article, @comment])
end
def test_with_nested_unsaved
expects(:article_comments_url).with(@article)
polymorphic_url([@article, @comment])
end
def test_new_with_array_and_namespace
expects(:new_admin_article_url).with()
polymorphic_url([:admin, @article], :action => 'new')
end
def test_unsaved_with_array_and_namespace
expects(:admin_articles_url).with()
polymorphic_url([:admin, @article])
end
def test_nested_unsaved_with_array_and_namespace
@article.save
expects(:admin_article_url).with(@article)
polymorphic_url([:admin, @article])
expects(:admin_article_comments_url).with(@article)
polymorphic_url([:admin, @article, @comment])
end
def test_nested_with_array_and_namespace
@comment.save
expects(:admin_article_comment_url).with(@article, @comment)
polymorphic_url([:admin, @article, @comment])
# a ridiculously long named route tests correct ordering of namespaces and nesting:
@tag = Tag.new
@tag.save
expects(:site_admin_article_comment_tag_url).with(@article, @comment, @tag)
polymorphic_url([:site, :admin, @article, @comment, @tag])
end
# TODO: Needs to be updated to correctly know about whether the object is in a hash or not
def xtest_with_hash
expects(:article_url).with(@article)
@article.save
polymorphic_url(:id => @article)
end
def test_polymorphic_path_accepts_options
expects(:new_article_path).with()
polymorphic_path(@article, :action => :new)
end
end
end

View File

@@ -77,6 +77,10 @@ class RedirectController < ActionController::Base
redirect_to Workshop.new(5, true)
end
def redirect_to_nil
redirect_to nil
end
def rescue_errors(e) raise e end
def rescue_action(e) raise end
@@ -215,6 +219,13 @@ class RedirectTest < Test::Unit::TestCase
get :redirect_to_new_record
assert_equal "http://test.host/workshops", redirect_to_url
end
def test_redirect_to_nil
assert_raises(ActionController::ActionControllerError) do
get :redirect_to_nil
end
end
end
module ModuleTest

View File

@@ -57,6 +57,14 @@ class TestController < ActionController::Base
render :text => "hello world", :status => 404
end
def render_text_with_nil
render :text => nil
end
def render_text_with_false
render :text => false
end
def render_nothing_with_appendix
render :text => "appended"
end
@@ -263,6 +271,17 @@ class RenderTest < Test::Unit::TestCase
assert_equal 'hello world', @response.body
end
def test_render_text_with_nil
get :render_text_with_nil
assert_response 200
assert_equal '', @response.body
end
def test_render_text_with_false
get :render_text_with_false
assert_equal 'false', @response.body
end
def test_render_nothing_with_appendix
get :render_nothing_with_appendix
assert_response 200

View File

@@ -22,6 +22,10 @@ module RequestForgeryProtectionActions
render :inline => "<%= button_to('New', '/') {} %>"
end
def remote_form
render :inline => "<% form_remote_tag(:url => '/') {} %>"
end
def unsafe
render :text => 'pwn'
end
@@ -75,6 +79,11 @@ module RequestForgeryProtectionTests
assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
end
def test_should_render_remote_form_with_only_one_token_parameter
get :remote_form
assert_equal 1, @response.body.scan(@token).size
end
def test_should_allow_get
get :index
assert_response :success

View File

@@ -13,9 +13,17 @@ class RequestTest < Test::Unit::TestCase
assert_equal '1.2.3.4', @request.remote_ip
@request.env['HTTP_CLIENT_IP'] = '2.3.4.5'
assert_equal '1.2.3.4', @request.remote_ip
@request.remote_addr = '192.168.0.1'
assert_equal '2.3.4.5', @request.remote_ip
@request.env.delete 'HTTP_CLIENT_IP'
@request.remote_addr = '1.2.3.4'
@request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6'
assert_equal '1.2.3.4', @request.remote_ip
@request.remote_addr = '127.0.0.1'
@request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6'
assert_equal '3.4.5.6', @request.remote_ip
@@ -35,10 +43,23 @@ class RequestTest < Test::Unit::TestCase
assert_equal '3.4.5.6', @request.remote_ip
@request.env['HTTP_X_FORWARDED_FOR'] = '127.0.0.1,3.4.5.6'
assert_equal '127.0.0.1', @request.remote_ip
assert_equal '3.4.5.6', @request.remote_ip
@request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,192.168.0.1'
assert_equal '1.2.3.4', @request.remote_ip
assert_equal 'unknown', @request.remote_ip
@request.env['HTTP_X_FORWARDED_FOR'] = '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4'
assert_equal '3.4.5.6', @request.remote_ip
@request.env['HTTP_CLIENT_IP'] = '8.8.8.8'
e = assert_raises(ActionController::ActionControllerError) {
@request.remote_ip
}
assert_match /IP spoofing attack/, e.message
assert_match /HTTP_X_FORWARDED_FOR="9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4"/, e.message
assert_match /HTTP_CLIENT_IP="8.8.8.8"/, e.message
@request.env.delete 'HTTP_CLIENT_IP'
@request.env.delete 'HTTP_X_FORWARDED_FOR'
end
@@ -371,6 +392,13 @@ class RequestTest < Test::Unit::TestCase
assert_equal Mime::HTML, @request.content_type
end
def test_format_assignment_should_set_format
@request.instance_eval { self.format = :txt }
assert !@request.format.xml?
@request.instance_eval { self.format = :xml }
assert @request.format.xml?
end
def test_content_no_type
assert_equal nil, @request.content_type
end

View File

@@ -2119,15 +2119,15 @@ class RoutingTest < Test::Unit::TestCase
end
def test_normalize_unix_paths
load_paths = %w(. config/../app/controllers config/../app//helpers script/../config/../vendor/rails/actionpack/lib vendor/rails/railties/builtin/rails_info app/models lib script/../config/../foo/bar/../../app/models)
load_paths = %w(. config/../app/controllers config/../app//helpers script/../config/../vendor/rails/actionpack/lib vendor/rails/railties/builtin/rails_info app/models lib script/../config/../foo/bar/../../app/models .foo/../.bar foo.bar/../config)
paths = ActionController::Routing.normalize_paths(load_paths)
assert_equal %w(vendor/rails/railties/builtin/rails_info vendor/rails/actionpack/lib app/controllers app/helpers app/models lib .), paths
assert_equal %w(vendor/rails/railties/builtin/rails_info vendor/rails/actionpack/lib app/controllers app/helpers app/models config .bar lib .), paths
end
def test_normalize_windows_paths
load_paths = %w(. config\\..\\app\\controllers config\\..\\app\\\\helpers script\\..\\config\\..\\vendor\\rails\\actionpack\\lib vendor\\rails\\railties\\builtin\\rails_info app\\models lib script\\..\\config\\..\\foo\\bar\\..\\..\\app\\models)
load_paths = %w(. config\\..\\app\\controllers config\\..\\app\\\\helpers script\\..\\config\\..\\vendor\\rails\\actionpack\\lib vendor\\rails\\railties\\builtin\\rails_info app\\models lib script\\..\\config\\..\\foo\\bar\\..\\..\\app\\models .foo\\..\\.bar foo.bar\\..\\config)
paths = ActionController::Routing.normalize_paths(load_paths)
assert_equal %w(vendor\\rails\\railties\\builtin\\rails_info vendor\\rails\\actionpack\\lib app\\controllers app\\helpers app\\models lib .), paths
assert_equal %w(vendor\\rails\\railties\\builtin\\rails_info vendor\\rails\\actionpack\\lib app\\controllers app\\helpers app\\models config .bar lib .), paths
end
def test_routing_helper_module

View File

@@ -4,11 +4,21 @@ require "action_controller/test_case"
class TestTest < Test::Unit::TestCase
class TestController < ActionController::Base
def no_op
render :text => 'dummy'
end
def set_flash
flash["test"] = ">#{flash["test"]}<"
render :text => 'ignore me'
end
def set_session
session['string'] = 'A wonder'
session[:symbol] = 'it works'
render :text => 'Success'
end
def render_raw_post
raise Test::Unit::AssertionFailedError, "#raw_post is blank" if request.raw_post.blank?
render :text => request.raw_post
@@ -136,6 +146,22 @@ XML
assert_equal '>value<', flash['test']
end
def test_process_with_session
process :set_session
assert_equal 'A wonder', session['string'], "A value stored in the session should be available by string key"
assert_equal 'A wonder', session[:string], "Test session hash should allow indifferent access"
assert_equal 'it works', session['symbol'], "Test session hash should allow indifferent access"
assert_equal 'it works', session[:symbol], "Test session hash should allow indifferent access"
end
def test_process_with_session_arg
process :no_op, nil, { 'string' => 'value1', :symbol => 'value2' }
assert_equal 'value1', session['string']
assert_equal 'value1', session[:string]
assert_equal 'value2', session['symbol']
assert_equal 'value2', session[:symbol]
end
def test_process_with_request_uri_with_no_params
process :test_uri
assert_equal "/test_test/test/test_uri", @response.body
@@ -375,6 +401,13 @@ XML
assert_routing 'content', :controller => 'content', :action => 'index'
end
def test_assert_routing_with_method
with_routing do |set|
set.draw { |map| map.resources(:content) }
assert_routing({ :method => 'post', :path => 'content' }, { :controller => 'content', :action => 'create' })
end
end
def test_assert_routing_in_module
assert_routing 'admin/user', :controller => 'admin/user', :action => 'index'
end
@@ -614,10 +647,19 @@ class InferringClassNameTest < Test::Unit::TestCase
end
end
class ContentControllerTest < ActionController::TestCase
def setup
# Should not override ActionController setup methods
end
def test_should_still_setup_controller
assert_kind_of(ContentController, @controller)
end
end
class CrazyNameTest < ActionController::TestCase
tests ContentController
def test_controller_class_can_be_set_manually_not_just_inferred
assert_equal ContentController, self.class.controller_class
end
end

View File

@@ -149,27 +149,57 @@ class UrlWriterTests < Test::Unit::TestCase
)
end
def test_named_route
def test_relative_url_root_is_respected
orig_relative_url_root = ActionController::AbstractRequest.relative_url_root
ActionController::AbstractRequest.relative_url_root = '/subdir'
add_host!
assert_equal('https://www.basecamphq.com/subdir/c/a/i',
W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https')
)
ensure
ActionController::AbstractRequest.relative_url_root = orig_relative_url_root
end
def test_named_routes
ActionController::Routing::Routes.draw do |map|
map.no_args '/this/is/verbose', :controller => 'home', :action => 'index'
map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'
map.connect ':controller/:action/:id'
end
# We need to create a new class in order to install the new named route.
kls = Class.new { include ActionController::UrlWriter }
controller = kls.new
assert controller.respond_to?(:home_url)
assert_equal 'http://www.basecamphq.com/home/sweet/home/again',
controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
assert_equal("/home/sweet/home/alabama", controller.send(:home_path, :user => 'alabama', :host => 'unused'))
assert_equal("http://www.basecamphq.com/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'www.basecamphq.com'))
assert_equal("http://www.basecamphq.com/this/is/verbose", controller.send(:no_args_url, :host=>'www.basecamphq.com'))
ensure
ActionController::Routing::Routes.load!
end
def test_relative_url_root_is_respected_for_named_routes
orig_relative_url_root = ActionController::AbstractRequest.relative_url_root
ActionController::AbstractRequest.relative_url_root = '/subdir'
ActionController::Routing::Routes.draw do |map|
map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'
end
kls = Class.new { include ActionController::UrlWriter }
controller = kls.new
assert_equal 'http://www.basecamphq.com/subdir/home/sweet/home/again',
controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again')
ensure
ActionController::Routing::Routes.load!
ActionController::AbstractRequest.relative_url_root = orig_relative_url_root
end
def test_only_path
ActionController::Routing::Routes.draw do |map|
map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index'

View File

@@ -0,0 +1,3 @@
<% render(:layout => "layout_for_partial", :locals => { :name => "Anthony" }) do %>Inside from first block in layout<% end %>
<%= yield %>
<% render(:layout => "layout_for_partial", :locals => { :name => "Ramm" }) do %>Inside from second block in layout<% end %>

View File

@@ -0,0 +1,3 @@
<%= render( :layout => "layout_for_partial", :partial => "partial_for_use_in_layout", :locals => {:name => 'Anthony' } ) %>
<%= yield %>
<%= render( :layout => "layout_for_partial", :partial => "partial_for_use_in_layout", :locals => {:name => 'Ramm' } ) %>

View File

@@ -392,6 +392,8 @@ class AssetTagHelperNonVhostTest < Test::Unit::TestCase
assert_dom_equal(%(/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
assert_dom_equal(%(/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
assert_dom_equal(%(/collaboration/hieraki/images/xml.png), image_path("xml.png"))
assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='/collaboration/hieraki/images/mouse.png'" src="/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='/collaboration/hieraki/images/mouse2.png'" src="/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
end
def test_should_ignore_relative_root_path_on_complete_url
@@ -404,6 +406,8 @@ class AssetTagHelperNonVhostTest < Test::Unit::TestCase
assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/images/xml.png), image_path("xml.png"))
assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='http://assets.example.com/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='http://assets.example.com/collaboration/hieraki/images/mouse.png'" src="http://assets.example.com/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='http://assets.example.com/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='http://assets.example.com/collaboration/hieraki/images/mouse2.png'" src="http://assets.example.com/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
ensure
ActionController::Base.asset_host = ""
end

View File

@@ -5,7 +5,7 @@ Scroll = Struct.new(:id, :to_param, :title, :body, :updated_at, :created_at)
class ScrollsController < ActionController::Base
FEEDS = {}
FEEDS["defaults"] = <<-EOT
atom_feed do |feed|
atom_feed(:schema_date => '2008') do |feed|
feed.title("My great blog!")
feed.updated((@scrolls.first.created_at))
@@ -38,7 +38,23 @@ class ScrollsController < ActionController::Base
end
end
EOT
FEEDS["xml_block"] = <<-EOT
atom_feed do |feed|
feed.title("My great blog!")
feed.updated((@scrolls.first.created_at))
feed.author do |author|
author.name("DHH")
end
for scroll in @scrolls
feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param, :updated => Time.utc(2007, 1, scroll.id)) do |entry|
entry.title(scroll.title)
entry.content(scroll.body, :type => 'html')
end
end
end
EOT
def index
@scrolls = [
Scroll.new(1, "1", "Hello One", "Something <i>COOL!</i>", Time.utc(2007, 12, 12, 15), Time.utc(2007, 12, 12, 15)),
@@ -47,6 +63,12 @@ class ScrollsController < ActionController::Base
render :inline => FEEDS[params[:id]], :type => :builder
end
protected
def rescue_action(e)
raise(e)
end
end
class AtomFeedTest < Test::Unit::TestCase
@@ -88,6 +110,34 @@ class AtomFeedTest < Test::Unit::TestCase
end
end
def test_self_url_should_default_to_current_request_url
with_restful_routing(:scrolls) do
get :index, :id => "defaults"
assert_select "link[rel=self][href=http://www.nextangle.com/scrolls?id=defaults]"
end
end
def test_feed_id_should_be_a_valid_tag
with_restful_routing(:scrolls) do
get :index, :id => "defaults"
assert_select "id", :text => "tag:www.nextangle.com,2008:/scrolls?id=defaults"
end
end
def test_entry_id_should_be_a_valid_tag
with_restful_routing(:scrolls) do
get :index, :id => "defaults"
assert_select "entry id", :text => "tag:www.nextangle.com,2008:Scroll/1"
assert_select "entry id", :text => "tag:www.nextangle.com,2008:Scroll/2"
end
end
def test_feed_should_allow_nested_xml_blocks
with_restful_routing(:scrolls) do
get :index, :id => "xml_block"
assert_select "author name", :text => "DHH"
end
end
private
def with_restful_routing(resources)
@@ -98,4 +148,4 @@ class AtomFeedTest < Test::Unit::TestCase
yield
end
end
end
end

View File

@@ -1395,7 +1395,7 @@ class DateHelperTest < Test::Unit::TestCase
expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
expected << %(<option value=""></option>\n)
2002.upto(2012) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
(Time.now.year - 5).upto(Time.now.year + 5) { |i| expected << %(<option value="#{i}">#{i}</option>\n) }
expected << "</select>\n"
expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
expected << %(<option value=""></option>\n)

View File

@@ -0,0 +1,51 @@
require File.dirname(__FILE__) + '/../abstract_unit'
class DeprecateIvars < ActionController::Base
def use_logger
render :inline => "<%= logger.class -%>"
end
def use_old_logger
render :inline => "<%= @logger.class -%>"
end
def use_action_name
render :inline => "<%= action_name -%>"
end
def use_old_action_name
render :inline => "<%= @action_name -%>"
end
end
class DeprecateIvarsTest < Test::Unit::TestCase
def setup
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@controller = DeprecateIvars.new
@controller.logger = Logger.new(nil)
@request.host = "rubyonrails.com"
end
def test_logger
assert_not_deprecated { get :use_logger }
assert_equal "Logger", @response.body
end
def test_deprecated_logger
assert_deprecated { get :use_old_logger }
assert_equal "Logger", @response.body
end
def test_action_name
assert_not_deprecated { get :use_action_name }
assert_equal "use_action_name", @response.body
end
def test_deprecated_action_name
assert_deprecated { get :use_old_action_name }
assert_equal "use_old_action_name", @response.body
end
end

View File

@@ -188,6 +188,24 @@ class FormTagHelperTest < Test::Unit::TestCase
assert_dom_equal expected, actual
end
def test_label_tag_without_text
actual = label_tag "title"
expected = %(<label for="title">Title</label>)
assert_dom_equal expected, actual
end
def test_label_tag_with_text
actual = label_tag "title", "My Title"
expected = %(<label for="title">My Title</label>)
assert_dom_equal expected, actual
end
def test_label_tag_class_string
actual = label_tag "title", "My Title", "class" => "small_label"
expected = %(<label for="title" class="small_label">My Title</label>)
assert_dom_equal expected, actual
end
def test_boolean_optios
assert_dom_equal %(<input checked="checked" disabled="disabled" id="admin" name="admin" readonly="readonly" type="checkbox" value="1" />), check_box_tag("admin", 1, true, 'disabled' => true, :readonly => "yes")
assert_dom_equal %(<input checked="checked" id="admin" name="admin" type="checkbox" value="1" />), check_box_tag("admin", 1, true, :disabled => false, :readonly => nil)

View File

@@ -87,6 +87,7 @@ class NumberHelperTest < Test::Unit::TestCase
assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, 4)
assert_equal '10 KB', number_to_human_size(10.000.kilobytes, 4)
assert_equal '1 Byte', number_to_human_size(1.1)
assert_equal '10 Bytes', number_to_human_size(10)
assert_nil number_to_human_size('x')
assert_nil number_to_human_size(nil)
end

View File

@@ -175,6 +175,7 @@ class TextHelperTest < Test::Unit::TestCase
http://www.rubyonrails.com/~minam/contact;new?with=query&string=params
http://en.wikipedia.org/wiki/Wikipedia:Today%27s_featured_picture_%28animation%29/January_20%2C_2007
http://www.mail-archive.com/rails@lists.rubyonrails.org/
http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1
)
urls.each do |url|

View File

@@ -1,4 +1,20 @@
*SVN*
*2.0.4* (2nd September 2008)
* Migrations: create_table supports primary_key_prefix_type. #10314 [student, thechrisoshow]
* Ensure that ActiveRecord::Calculations disambiguates field names with the table name. #11027 [cavalle]
* Ensure that modifying has_and_belongs_to_many actions clear the query cache. Closes #10840 [john.andrews]
* Fix issue where Table#references doesn't pass a :null option to a *_type attribute for polymorphic associations. Closes #10753 [railsjitsu]
* update_all ignores scoped :order and :limit, so post.comments.update_all doesn't try to include the comment order in the update statement. #10686 [Brendan Ribera]
* Added by parameter to increment, decrement, and their bang varieties so you can do player1.increment!(:points, 5) #10542 [Sam]
* Optimize ActiveRecord::Base#exists? to use #select_all instead of #find. Closes #10605 [jamesh, fcheung, protocool]
* Don't unnecessarily load has_many associations in after_update callbacks. Closes #6822 [stopdropandrew, canadaduane]
* Eager belongs_to :include infers the foreign key from the association name rather than the class name. #10517 [Jonathan Viney]

View File

@@ -4,6 +4,7 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
require 'rake/contrib/sshpublisher'
require 'rake/contrib/rubyforgepublisher'
require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
@@ -173,7 +174,7 @@ spec = Gem::Specification.new do |s|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
end
s.add_dependency('activesupport', '= 2.0.2' + PKG_BUILD)
s.add_dependency('activesupport', '= 2.0.4' + PKG_BUILD)
s.files.delete "test/fixtures/fixture_database.sqlite"
s.files.delete "test/fixtures/fixture_database_2.sqlite"
@@ -227,8 +228,8 @@ end
desc "Publish the beta gem"
task :pgem => [:package] do
Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
`ssh davidhh@wrath.rubyonrails.org './gemupdate.sh'`
Rake::SshFilePublisher.new("david@greed.loudthinking.com", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
`ssh david@greed.loudthinking.com '/u/sites/gems/gemupdate.sh'`
end
desc "Publish the API documentation"
@@ -239,6 +240,7 @@ end
desc "Publish the release files to RubyForge."
task :release => [ :package ] do
require 'rubyforge'
require 'rake/contrib/rubyforgepublisher'
packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }

View File

@@ -130,7 +130,7 @@ module ActiveRecord
# #others<< | X | X | X
# #others.push | X | X | X
# #others.concat | X | X | X
# #others.build(attributes={}) | X | X | X
# #others.build(attributes={}) | X | X |
# #others.create(attributes={}) | X | X |
# #others.create!(attributes={}) | X | X | X
# #others.size | X | X | X
@@ -487,6 +487,22 @@ module ActiveRecord
# When eager loaded, conditions are interpolated in the context of the model class, not the model instance. Conditions are lazily interpolated
# before the actual model exists.
#
# Eager loading is not possible with polymorphic associations. Given
#
# class Address < ActiveRecord::Base
# belongs_to :addressable, :polymorphic => true
# end
#
# a call that tries to eager load the addressable model
#
# Address.find(:all, :include => :addressable) # INVALID
#
# will raise <tt>ActiveRecord::EagerLoadPolymorphicError</tt>. The reason is that the parent model's type
# is a column value so its corresponding table name cannot be put in the FROM/JOIN clauses of that early query.
#
# It does work the other way around though: if the <tt>User</tt> model is <tt>addressable</tt> you can eager load
# their addresses with <tt>:include</tt> just fine, every piece needed to construct the query is known beforehand.
#
# == Table Aliasing
#
# ActiveRecord uses table aliasing in the case that a table is referenced multiple times in a join. If a table is referenced only once,
@@ -783,6 +799,7 @@ module ActiveRecord
# a column name instead of a +true+/+false+ value to this option (e.g., <tt>:counter_cache => :my_custom_counter</tt>.)
# Note: Specifying a counter_cache will add it to that model's list of readonly attributes using #attr_readonly.
# * <tt>:include</tt> - specify second-order associations that should be eager loaded when this object is loaded.
# Not allowed if the association is polymorphic.
# * <tt>:polymorphic</tt> - specify this association is a polymorphic association by passing +true+.
# Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
# to the attr_readonly list in the associated classes (e.g. class Post; attr_readonly :comments_count; end).
@@ -1079,8 +1096,10 @@ module ActiveRecord
if association.respond_to?(:loaded?)
if new_record?
association
else
elsif association.loaded?
association.select { |record| record.new_record? }
else
association.target.select { |record| record.new_record? }
end.each do |record|
errors.add "#{association_name}" unless record.valid?
end
@@ -1097,6 +1116,8 @@ module ActiveRecord
association
elsif association.respond_to?(:loaded?) && association.loaded?
association.select { |record| record.new_record? }
elsif association.respond_to?(:loaded?) && !association.loaded?
association.target.select { |record| record.new_record? }
else
[]
end

View File

@@ -14,7 +14,7 @@ module ActiveRecord
def create(attributes = {})
create_record(attributes) { |record| insert_record(record) }
end
def create!(attributes = {})
create_record(attributes) { |record| insert_record(record, true) }
end
@@ -80,7 +80,7 @@ module ActiveRecord
end
if @reflection.options[:insert_sql]
@owner.connection.execute(interpolate_sql(@reflection.options[:insert_sql], record))
@owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record))
else
columns = @owner.connection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns")
@@ -103,7 +103,7 @@ module ActiveRecord
"INSERT INTO #{@reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " +
"VALUES (#{attributes.values.join(', ')})"
@owner.connection.execute(sql)
@owner.connection.insert(sql)
end
return true
@@ -111,11 +111,11 @@ module ActiveRecord
def delete_records(records)
if sql = @reflection.options[:delete_sql]
records.each { |record| @owner.connection.execute(interpolate_sql(sql, record)) }
records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) }
else
ids = quoted_record_ids(records)
sql = "DELETE FROM #{@reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})"
@owner.connection.execute(sql)
sql = "DELETE FROM #{@owner.connection.quote_table_name @reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})"
@owner.connection.delete(sql)
end
end

View File

@@ -548,8 +548,14 @@ module ActiveRecord #:nodoc:
# Person.exists?(:name => "David")
# Person.exists?(['name LIKE ?', "%#{query}%"])
def exists?(id_or_conditions)
!find(:first, :select => "#{quoted_table_name}.#{primary_key}",
:conditions => expand_id_conditions(id_or_conditions)).nil?
connection.select_all(
construct_finder_sql(
:select => "#{quoted_table_name}.#{primary_key}",
:conditions => expand_id_conditions(id_or_conditions),
:limit => 1
),
"#{name} Exists"
).size > 0
end
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -674,8 +680,8 @@ module ActiveRecord #:nodoc:
sql = "UPDATE #{table_name} SET #{sanitize_sql_for_assignment(updates)} "
scope = scope(:find)
add_conditions!(sql, conditions, scope)
add_order!(sql, options[:order], scope)
add_limit!(sql, options, scope)
add_order!(sql, options[:order], nil)
add_limit!(sql, options, nil)
connection.update(sql, "#{name} Update")
end
@@ -960,14 +966,19 @@ module ActiveRecord #:nodoc:
end
def reset_primary_key #:nodoc:
key = get_primary_key(base_class.name)
set_primary_key(key)
key
end
def get_primary_key(base_name) #:nodoc:
key = 'id'
case primary_key_prefix_type
when :table_name
key = Inflector.foreign_key(base_class.name, false)
key = Inflector.foreign_key(base_name, false)
when :table_name_with_underscore
key = Inflector.foreign_key(base_class.name)
key = Inflector.foreign_key(base_name)
end
set_primary_key(key)
key
end
@@ -1487,7 +1498,7 @@ module ActiveRecord #:nodoc:
self.class_eval %{
def self.#{method_id}(*args)
options = args.last.is_a?(Hash) ? args.pop : {}
options = args.extract_options!
attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
finder_options = { :conditions => attributes }
validate_find_options(options)
@@ -2046,28 +2057,28 @@ module ActiveRecord #:nodoc:
save!
end
# Initializes the +attribute+ to zero if nil and adds one. Only makes sense for number-based attributes. Returns self.
def increment(attribute)
# Initializes the +attribute+ to zero if nil and adds the value passed as +by+ (default is one). Only makes sense for number-based attributes. Returns self.
def increment(attribute, by = 1)
self[attribute] ||= 0
self[attribute] += 1
self[attribute] += by
self
end
# Increments the +attribute+ and saves the record.
def increment!(attribute)
increment(attribute).update_attribute(attribute, self[attribute])
def increment!(attribute, by = 1)
increment(attribute, by).update_attribute(attribute, self[attribute])
end
# Initializes the +attribute+ to zero if nil and subtracts one. Only makes sense for number-based attributes. Returns self.
def decrement(attribute)
# Initializes the +attribute+ to zero if nil and subtracts the value passed as +by+ (default is one). Only makes sense for number-based attributes. Returns self.
def decrement(attribute, by = 1)
self[attribute] ||= 0
self[attribute] -= 1
self[attribute] -= by
self
end
# Decrements the +attribute+ and saves the record.
def decrement!(attribute)
decrement(attribute).update_attribute(attribute, self[attribute])
def decrement!(attribute, by = 1)
decrement(attribute, by).update_attribute(attribute, self[attribute])
end
# Turns an +attribute+ that's currently true into false and vice versa. Returns self.
@@ -2134,6 +2145,7 @@ module ActiveRecord #:nodoc:
if options.nil?
attributes
else
ActiveSupport::Deprecation.warn "Passing options to Base#attributes is deprecated and will be removed in Rails 2.1. Please use Hash#slice or Hash#except instead"
if except = options[:except]
except = Array(except).collect { |attribute| attribute.to_s }
except.each { |attribute_name| attributes.delete(attribute_name) }
@@ -2385,6 +2397,10 @@ module ActiveRecord #:nodoc:
end
# Includes an ugly hack for Time.local instead of Time.new because the latter is reserved by Time itself.
def instantiate_time_object(*values)
@@default_timezone == :utc ? Time.utc(*values) : Time.local(*values)
end
def execute_callstack_for_multiparameter_attributes(callstack)
errors = []
callstack.each do |name, values|
@@ -2393,7 +2409,19 @@ module ActiveRecord #:nodoc:
send(name + "=", nil)
else
begin
send(name + "=", Time == klass ? (@@default_timezone == :utc ? klass.utc(*values) : klass.local(*values)) : klass.new(*values))
value = if Time == klass
instantiate_time_object(*values)
elsif Date == klass
begin
Date.new(*values)
rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
instantiate_time_object(*values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
end
else
klass.new(*values)
end
send(name + "=", value)
rescue => ex
errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
end

View File

@@ -155,6 +155,7 @@ module ActiveRecord
scope = scope(:find)
merged_includes = merge_includes(scope ? scope[:include] : [], options[:include])
aggregate_alias = column_alias_for(operation, column_name)
column_name = "#{connection.quote_table_name(table_name)}.#{column_name}" unless column_name == "*" || column_name.to_s.include?('.')
if operation == 'count'
if merged_includes.any?
@@ -213,7 +214,7 @@ module ActiveRecord
group_attr = options[:group].to_s
association = reflect_on_association(group_attr.to_sym)
associated = association && association.macro == :belongs_to # only count belongs_to associations
group_field = (associated ? "#{options[:group]}_id" : options[:group]).to_s
group_field = associated ? association.primary_key_name : group_attr
group_alias = column_alias_for(group_field)
group_column = column_for group_field
sql = construct_calculation_sql(operation, column_name, options.merge(:group_field => group_field, :group_alias => group_alias))

View File

@@ -453,9 +453,7 @@ module ActiveRecord
polymorphic = options.delete(:polymorphic)
args.each do |col|
column("#{col}_id", :integer, options)
unless polymorphic.nil?
column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : {})
end
column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
end
end
alias :belongs_to :references

View File

@@ -89,7 +89,7 @@ module ActiveRecord
# See also TableDefinition#column for details on how to create columns.
def create_table(table_name, options = {})
table_definition = TableDefinition.new(self)
table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name)) unless options[:id] == false
yield table_definition

View File

@@ -346,7 +346,7 @@ module ActiveRecord
# There are some incorrectly compiled postgres drivers out there
# that don't define PGconn.escape.
self.class.instance_eval do
undef_method(:quote_string)
remove_method(:quote_string)
end
end
quote_string(s)
@@ -386,8 +386,8 @@ module ActiveRecord
# Executes an INSERT query and returns the new record's ID
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
table = sql.split(" ", 4)[2]
super || last_insert_id(table, sequence_name || default_sequence_name(table, pk))
table = sql.split(" ", 4)[2].gsub('"', '')
super || pk && last_insert_id(table, sequence_name || default_sequence_name(table, pk))
end
# Queries the database and returns the results in an Array or nil otherwise.

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