aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionmailer/CHANGELOG17
-rw-r--r--actionmailer/actionmailer.gemspec2
-rw-r--r--actionmailer/lib/action_mailer.rb1
-rw-r--r--actionmailer/lib/action_mailer/base.rb54
-rw-r--r--actionmailer/lib/action_mailer/old_api.rb17
-rw-r--r--actionmailer/lib/action_mailer/quoting.rb64
-rw-r--r--actionmailer/lib/action_mailer/test_case.rb6
-rw-r--r--actionmailer/test/base_test.rb10
-rw-r--r--actionmailer/test/old_base/mail_service_test.rb85
-rw-r--r--actionmailer/test/old_base/url_test.rb5
-rw-r--r--actionmailer/test/quoting_test.rb106
-rw-r--r--actionmailer/test/test_helper_test.rb6
-rw-r--r--actionpack/CHANGELOG9
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb3
-rw-r--r--actionpack/lib/action_controller/deprecated/base.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb23
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb181
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb26
-rw-r--r--actionpack/lib/action_view/lookup_context.rb13
-rw-r--r--actionpack/test/controller/layout_test.rb2
-rw-r--r--actionpack/test/controller/new_base/content_type_test.rb10
-rw-r--r--actionpack/test/dispatch/routing_test.rb25
-rw-r--r--actionpack/test/fixtures/test/array_translation.erb2
-rw-r--r--actionpack/test/template/form_helper_test.rb5
-rw-r--r--actionpack/test/template/number_helper_test.rb1
-rw-r--r--actionpack/test/template/translation_helper_test.rb17
-rw-r--r--activemodel/CHANGELOG5
-rw-r--r--activerecord/CHANGELOG2
-rwxr-xr-xactiverecord/lib/active_record/associations.rb7
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb23
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb4
-rwxr-xr-xactiverecord/lib/active_record/base.rb51
-rw-r--r--activerecord/lib/active_record/callbacks.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb6
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/abstract_adapter.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb69
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb14
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb9
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb3
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb4
-rw-r--r--activerecord/lib/rails/generators/active_record/model/model_generator.rb5
-rw-r--r--activerecord/lib/rails/generators/active_record/model/templates/module.rb5
-rwxr-xr-xactiverecord/test/cases/base_test.rb70
-rw-r--r--activerecord/test/cases/datatype_test_postgresql.rb2
-rw-r--r--activerecord/test/cases/modules_test.rb33
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb12
-rw-r--r--activerecord/test/cases/schema_test_postgresql.rb15
-rw-r--r--activerecord/test/fixtures/collections.yml3
-rw-r--r--activerecord/test/fixtures/products.yml4
-rw-r--r--activerecord/test/fixtures/variants.yml4
-rw-r--r--activerecord/test/models/shop.rb12
-rw-r--r--activerecord/test/schema/schema.rb14
-rw-r--r--activeresource/CHANGELOG5
-rw-r--r--activesupport/CHANGELOG4
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute.rb39
-rw-r--r--activesupport/lib/active_support/core_ext/proc.rb2
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb12
-rw-r--r--activesupport/lib/active_support/inflector/transliterate.rb61
-rw-r--r--activesupport/test/inflector_test_cases.rb5
-rw-r--r--activesupport/test/transliterate_test.rb50
-rwxr-xr-xbin/rails7
-rw-r--r--ci/site_config.rb2
-rw-r--r--rails.gemspec9
-rw-r--r--railties/CHANGELOG3
-rw-r--r--railties/guides/source/initialization.textile2
-rw-r--r--railties/guides/source/routing.textile4
-rw-r--r--[-rwxr-xr-x]railties/lib/rails/cli.rb (renamed from railties/bin/rails)0
-rw-r--r--railties/lib/rails/commands/runner.rb2
-rw-r--r--railties/lib/rails/engine.rb2
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb17
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/boot.rb21
-rw-r--r--railties/lib/rails/generators/rails/model/USAGE15
-rw-r--r--railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css6
-rw-r--r--railties/lib/rails/test_unit/testing.rake2
-rw-r--r--railties/railties.gemspec3
-rw-r--r--railties/test/application/configuration_test.rb36
-rw-r--r--railties/test/generators/model_generator_test.rb8
-rw-r--r--railties/test/isolation/abstract_unit.rb2
-rw-r--r--railties/test/railties/shared_tests.rb2
-rw-r--r--release.rb11
84 files changed, 857 insertions, 560 deletions
diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG
index 605b18d7e9..383ad2105d 100644
--- a/actionmailer/CHANGELOG
+++ b/actionmailer/CHANGELOG
@@ -1,3 +1,10 @@
+*Rails 3.0.0 [beta 3] (April 13th, 2010)*
+
+* Removed all quoting.rb type files from ActionMailer and put Mail 2.2.0 in instead [ML]
+
+* Lot of updates to various test cases that now work better with the new Mail and so have different expectations
+
+
*Rails 3.0.0 [beta 2] (April 1st, 2010)*
* Added interceptors and observers from Mail [ML]
@@ -5,6 +12,16 @@
ActionMailer::Base.register_interceptor calls Mail.register_interceptor
ActionMailer::Base.register_observer calls Mail.register_observer
+* Mail::Part now no longer has nil as a default charset, it is always set to something, and defaults to UTF-8
+
+* Added explict setting of charset in set_fields! method to make sure Mail has the user defined default
+
+* Removed quoting.rb and refactored for Mail to take responsibility of all quoting and auto encoding requirements for the header.
+
+* Fixed several tests which had incorrect encoding.
+
+* Changed all utf-8 to UTF-8 for consistency
+
* Whole new API added with tests. See base.rb for full details. Old API is deprecated.
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index 410df0e106..01a886c5ee 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -20,6 +20,6 @@ Gem::Specification.new do |s|
s.has_rdoc = true
s.add_dependency('actionpack', version)
- s.add_dependency('mail', '~> 2.1.5.3')
+ s.add_dependency('mail', '~> 2.2.0')
s.add_dependency('text-format', '~> 1.0.0')
end
diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb
index 43d73e71b9..46168d9a4a 100644
--- a/actionmailer/lib/action_mailer.rb
+++ b/actionmailer/lib/action_mailer.rb
@@ -46,7 +46,6 @@ module ActionMailer
autoload :DeprecatedApi
autoload :MailHelper
autoload :OldApi
- autoload :Quoting
autoload :TestCase
autoload :TestHelper
end
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index af7255ae4f..d827ccdf2b 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -207,7 +207,7 @@ module ActionMailer #:nodoc:
# scores instead of hyphens, so <tt>Content-Transfer-Encoding:</tt>
# becomes <tt>:content_transfer_encoding</tt>. The defaults set by Action Mailer are:
# * <tt>:mime_version => "1.0"</tt>
- # * <tt>:charset => "utf-8",</tt>
+ # * <tt>:charset => "UTF-8",</tt>
# * <tt>:content_type => "text/plain",</tt>
# * <tt>:parts_order => [ "text/plain", "text/enriched", "text/html" ]</tt>
#
@@ -264,7 +264,7 @@ module ActionMailer #:nodoc:
# (i.e. multiple parts are assembled from templates which specify the content type in their
# filenames) this variable controls how the parts are ordered.
class Base < AbstractController::Base
- include DeliveryMethods, Quoting
+ include DeliveryMethods
abstract!
include AbstractController::Logger
@@ -286,7 +286,7 @@ module ActionMailer #:nodoc:
class_attribute :default_params
self.default_params = {
:mime_version => "1.0",
- :charset => "utf-8",
+ :charset => "UTF-8",
:content_type => "text/plain",
:parts_order => [ "text/plain", "text/enriched", "text/html" ]
}.freeze
@@ -375,6 +375,11 @@ module ActionMailer #:nodoc:
process(method_name, *args) if method_name
end
+ def process(*args) #:nodoc:
+ lookup_context.skip_default_locale!
+ super
+ end
+
# Allows you to pass random and unusual headers to the new +Mail::Message+ object
# which will add them to itself.
#
@@ -525,19 +530,25 @@ module ActionMailer #:nodoc:
content_type = headers[:content_type]
parts_order = headers[:parts_order]
- # Merge defaults from class
+ # Handle defaults
headers = headers.reverse_merge(self.class.default)
- charset = headers.delete(:charset)
-
- # Quote fields
headers[:subject] ||= default_i18n_subject
- quote_fields!(headers, charset)
+
+ # Apply charset at the beginning so all fields are properly quoted
+ m.charset = charset = headers[:charset]
+
+ # Set configure delivery behavior
+ wrap_delivery_behavior!(headers.delete(:delivery_method))
+
+ # Assign all headers except parts_order, content_type and body
+ assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path)
+ assignable.each { |k, v| m[k] = v }
# Render the templates and blocks
responses, explicit_order = collect_responses_and_parts_order(headers, &block)
- create_parts_from_responses(m, responses, charset)
+ create_parts_from_responses(m, responses)
- # Finally setup content type and parts order
+ # Setup content type, reapply charset and handle parts order
m.content_type = set_content_type(m, content_type, headers[:content_type])
m.charset = charset
@@ -547,12 +558,6 @@ module ActionMailer #:nodoc:
m.body.sort_parts!
end
- # Set configure delivery behavior
- wrap_delivery_behavior!(headers.delete(:delivery_method))
-
- # Remove any missing configuration header and assign all others
- headers.except!(:parts_order, :content_type)
- headers.each { |k, v| m[k] = v }
m
end
@@ -577,17 +582,6 @@ module ActionMailer #:nodoc:
I18n.t(:subject, :scope => [:actionmailer, mailer_scope, action_name], :default => action_name.humanize)
end
- # TODO: Move this into Mail
- def quote_fields!(headers, charset) #:nodoc:
- m = @_message
- m.subject ||= quote_if_necessary(headers.delete(:subject), charset) if headers[:subject]
- m.to ||= quote_address_if_necessary(headers.delete(:to), charset) if headers[:to]
- m.from ||= quote_address_if_necessary(headers.delete(:from), charset) if headers[:from]
- m.cc ||= quote_address_if_necessary(headers.delete(:cc), charset) if headers[:cc]
- m.bcc ||= quote_address_if_necessary(headers.delete(:bcc), charset) if headers[:bcc]
- m.reply_to ||= quote_address_if_necessary(headers.delete(:reply_to), charset) if headers[:reply_to]
- end
-
def collect_responses_and_parts_order(headers) #:nodoc:
responses, parts_order = [], nil
@@ -630,16 +624,16 @@ module ActionMailer #:nodoc:
end
end
- def create_parts_from_responses(m, responses, charset) #:nodoc:
+ def create_parts_from_responses(m, responses) #:nodoc:
if responses.size == 1 && !m.has_attachments?
responses[0].each { |k,v| m[k] = v }
elsif responses.size > 1 && m.has_attachments?
container = Mail::Part.new
container.content_type = "multipart/alternative"
- responses.each { |r| insert_part(container, r, charset) }
+ responses.each { |r| insert_part(container, r, m.charset) }
m.add_part(container)
else
- responses.each { |r| insert_part(m, r, charset) }
+ responses.each { |r| insert_part(m, r, m.charset) }
end
end
diff --git a/actionmailer/lib/action_mailer/old_api.rb b/actionmailer/lib/action_mailer/old_api.rb
index 7c59a8ae50..b2111209c7 100644
--- a/actionmailer/lib/action_mailer/old_api.rb
+++ b/actionmailer/lib/action_mailer/old_api.rb
@@ -143,12 +143,12 @@ module ActionMailer
{ :content_type => content_type,
:content_disposition => content_disposition }.merge(params)
end
-
+
def create_mail
m = @_message
- quote_fields!({:subject => subject, :to => recipients, :from => from,
- :bcc => bcc, :cc => cc, :reply_to => reply_to}, charset)
+ set_fields!({:subject => subject, :to => recipients, :from => from,
+ :bcc => bcc, :cc => cc, :reply_to => reply_to}, charset)
m.mime_version = mime_version unless mime_version.nil?
m.date = sent_on.to_time rescue sent_on if sent_on
@@ -230,6 +230,17 @@ module ActionMailer
)
end
+ def set_fields!(headers, charset) #:nodoc:
+ m = @_message
+ m.charset = charset
+ m.subject ||= headers.delete(:subject) if headers[:subject]
+ m.to ||= headers.delete(:to) if headers[:to]
+ m.from ||= headers.delete(:from) if headers[:from]
+ m.cc ||= headers.delete(:cc) if headers[:cc]
+ m.bcc ||= headers.delete(:bcc) if headers[:bcc]
+ m.reply_to ||= headers.delete(:reply_to) if headers[:reply_to]
+ end
+
def split_content_type(ct)
ct.to_s.split("/")
end
diff --git a/actionmailer/lib/action_mailer/quoting.rb b/actionmailer/lib/action_mailer/quoting.rb
deleted file mode 100644
index 2b55c0f0ab..0000000000
--- a/actionmailer/lib/action_mailer/quoting.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-module ActionMailer
- module Quoting #:nodoc:
- # TODO extract this into Mail itself.
- #
- #
- # Convert the given text into quoted printable format, with an instruction
- # that the text be eventually interpreted in the given charset.
- def quoted_printable(text, charset)
- text = text.gsub( /[^a-z ]/i ) { quoted_printable_encode($&) }.
- gsub( / /, "_" )
- "=?#{charset}?Q?#{text}?="
- end
-
- # Convert the given character to quoted printable format, taking into
- # account multi-byte characters (if executing with $KCODE="u", for instance)
- def quoted_printable_encode(character)
- result = ""
- character.each_byte { |b| result << "=%02X" % b }
- result
- end
-
- # A quick-and-dirty regexp for determining whether a string contains any
- # characters that need escaping.
- if !defined?(CHARS_NEEDING_QUOTING)
- CHARS_NEEDING_QUOTING = Regexp.new('[\000-\011\013\014\016-\037\177-\377]', nil, 'n')
- end
-
- # Quote the given text if it contains any "illegal" characters
- def quote_if_necessary(text, charset)
- text = text.dup.force_encoding(Encoding::ASCII_8BIT) if text.respond_to?(:force_encoding)
-
- (text =~ CHARS_NEEDING_QUOTING) ?
- quoted_printable(text, charset) :
- text
- end
-
- # Quote any of the given strings if they contain any "illegal" characters
- def quote_any_if_necessary(charset, *args)
- args.map { |v| quote_if_necessary(v, charset) }
- end
-
- # Quote the given address if it needs to be. The address may be a
- # regular email address, or it can be a phrase followed by an address in
- # brackets. The phrase is the only part that will be quoted, and only if
- # it needs to be. This allows extended characters to be used in the
- # "to", "from", "cc", "bcc" and "reply-to" headers.
- def quote_address_if_necessary(address, charset)
- if Array === address
- address.map { |a| quote_address_if_necessary(a, charset) }.join(", ")
- elsif address =~ /^(\S.*)\s+(<.*>)$/
- address = $2
- phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
- "\"#{phrase}\" #{address}"
- else
- address
- end
- end
-
- # Quote any of the given addresses, if they need to be.
- def quote_any_address_if_necessary(charset, *args)
- args.map { |v| quote_address_if_necessary(v, charset) }
- end
- end
-end
diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb
index 7c4033a125..d4874c6dbf 100644
--- a/actionmailer/lib/action_mailer/test_case.rb
+++ b/actionmailer/lib/action_mailer/test_case.rb
@@ -8,7 +8,7 @@ module ActionMailer
end
class TestCase < ActiveSupport::TestCase
- include Quoting, TestHelper
+ include TestHelper
setup :initialize_test_deliveries
setup :set_expected_mail
@@ -48,11 +48,11 @@ module ActionMailer
private
def charset
- "utf-8"
+ "UTF-8"
end
def encode(subject)
- quoted_printable(subject, charset)
+ Mail::Encodings.q_value_encode(subject, charset)
end
def read_fixture(action)
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index baeee542be..8e69073009 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -510,28 +510,28 @@ class BaseTest < ActiveSupport::TestCase
# Rendering
test "you can specify a different template for implicit render" do
- mail = BaseMailer.implicit_different_template('implicit_multipart')
+ mail = BaseMailer.implicit_different_template('implicit_multipart').deliver
assert_equal("HTML Implicit Multipart", mail.html_part.body.decoded)
assert_equal("TEXT Implicit Multipart", mail.text_part.body.decoded)
end
test "you can specify a different template for explicit render" do
- mail = BaseMailer.explicit_different_template('explicit_multipart_templates')
+ mail = BaseMailer.explicit_different_template('explicit_multipart_templates').deliver
assert_equal("HTML Explicit Multipart Templates", mail.html_part.body.decoded)
assert_equal("TEXT Explicit Multipart Templates", mail.text_part.body.decoded)
end
test "you can specify a different layout" do
- mail = BaseMailer.different_layout('different_layout')
+ mail = BaseMailer.different_layout('different_layout').deliver
assert_equal("HTML -- HTML", mail.html_part.body.decoded)
assert_equal("PLAIN -- PLAIN", mail.text_part.body.decoded)
end
test "you can specify the template path for implicit lookup" do
- mail = BaseMailer.welcome_from_another_path('another.path/base_mailer')
+ mail = BaseMailer.welcome_from_another_path('another.path/base_mailer').deliver
assert_equal("Welcome from another path", mail.body.encoded)
- mail = BaseMailer.welcome_from_another_path(['unknown/invalid', 'another.path/base_mailer'])
+ mail = BaseMailer.welcome_from_another_path(['unknown/invalid', 'another.path/base_mailer']).deliver
assert_equal("Welcome from another path", mail.body.encoded)
end
diff --git a/actionmailer/test/old_base/mail_service_test.rb b/actionmailer/test/old_base/mail_service_test.rb
index 9eb067554f..db2db59cc7 100644
--- a/actionmailer/test/old_base/mail_service_test.rb
+++ b/actionmailer/test/old_base/mail_service_test.rb
@@ -105,7 +105,7 @@ class TestMailer < ActionMailer::Base
sent_on Time.local(2004, 12, 12)
cc "Foo áëô îü <extended@example.net>"
bcc "Foo áëô îü <extended@example.net>"
- charset "utf-8"
+ charset "UTF-8"
body "åœö blah"
end
@@ -131,7 +131,7 @@ class TestMailer < ActionMailer::Base
recipients recipient
subject "Foo áëô îü"
from "test@example.com"
- charset "utf-8"
+ charset "UTF-8"
part "text/plain" do |p|
p.body = "blah"
@@ -316,18 +316,15 @@ class TestMailer < ActionMailer::Base
end
class ActionMailerTest < Test::Unit::TestCase
- include ActionMailer::Quoting
- def encode( text, charset="utf-8" )
- quoted_printable( text, charset )
+ def encode( text, charset="UTF-8" )
+ Mail::Encodings.q_value_encode( text, charset )
end
- def new_mail( charset="utf-8" )
+ def new_mail( charset="UTF-8" )
mail = Mail.new
+ mail.charset = charset
mail.mime_version = "1.0"
- if charset
- mail.content_type ["text", "plain", { "charset" => charset }]
- end
mail
end
@@ -358,7 +355,7 @@ class ActionMailerTest < Test::Unit::TestCase
assert_equal "multipart/mixed", created.mime_type
assert_equal "multipart/alternative", created.parts[0].mime_type
assert_equal "bar", created.parts[0].header['foo'].to_s
- assert_nil created.parts[0].charset
+ assert_not_nil created.parts[0].charset
assert_equal "text/plain", created.parts[0].parts[0].mime_type
assert_equal "text/html", created.parts[0].parts[1].mime_type
assert_equal "application/octet-stream", created.parts[1].mime_type
@@ -570,7 +567,6 @@ class ActionMailerTest < Test::Unit::TestCase
def test_iso_charset
TestMailer.delivery_method = :test
-
expected = new_mail( "iso-8859-1" )
expected.to = @recipient
expected.subject = encode "testing isø charsets", "iso-8859-1"
@@ -671,14 +667,14 @@ class ActionMailerTest < Test::Unit::TestCase
def test_unquote_quoted_printable_subject
msg = <<EOF
From: me@example.com
-Subject: =?utf-8?Q?testing_testing_=D6=A4?=
+Subject: =?UTF-8?Q?testing_testing_=D6=A4?=
Content-Type: text/plain; charset=iso-8859-1
The body
EOF
mail = Mail.new(msg)
assert_equal "testing testing \326\244", mail.subject
- assert_equal "Subject: =?utf-8?Q?testing_testing_=D6=A4?=\r\n", mail[:subject].encoded
+ assert_equal "Subject: testing testing =?UTF-8?Q?_=D6=A4=?=\r\n", mail[:subject].encoded
end
def test_unquote_7bit_subject
@@ -719,7 +715,7 @@ The=3Dbody
EOF
mail = Mail.new(msg)
assert_equal "The=body", mail.body.to_s.strip
- assert_equal "The=3Dbody", mail.body.encoded.strip
+ assert_equal "The=3Dbody=", mail.body.encoded.strip
end
def test_unquote_base64_body
@@ -740,12 +736,12 @@ EOF
@recipient = "Grytøyr <test@localhost>"
expected = new_mail "iso-8859-1"
- expected.to = quote_address_if_necessary @recipient, "iso-8859-1"
+ expected.to = @recipient
expected.subject = "testing extended headers"
expected.body = "Nothing to see here."
- expected.from = quote_address_if_necessary "Grytøyr <stian1@example.net>", "iso-8859-1"
- expected.cc = quote_address_if_necessary "Grytøyr <stian2@example.net>", "iso-8859-1"
- expected.bcc = quote_address_if_necessary "Grytøyr <stian3@example.net>", "iso-8859-1"
+ expected.from = "Grytøyr <stian1@example.net>"
+ expected.cc = "Grytøyr <stian2@example.net>"
+ expected.bcc = "Grytøyr <stian3@example.net>"
expected.date = Time.local 2004, 12, 12
created = nil
@@ -774,13 +770,13 @@ EOF
def test_utf8_body_is_not_quoted
@recipient = "Foo áëô îü <extended@example.net>"
- expected = new_mail "utf-8"
- expected.to = quote_address_if_necessary @recipient, "utf-8"
- expected.subject = "testing utf-8 body"
+ expected = new_mail "UTF-8"
+ expected.to = @recipient
+ expected.subject = "testing UTF-8 body"
expected.body = "åœö blah"
- expected.from = quote_address_if_necessary @recipient, "utf-8"
- expected.cc = quote_address_if_necessary @recipient, "utf-8"
- expected.bcc = quote_address_if_necessary @recipient, "utf-8"
+ expected.from = @recipient
+ expected.cc = @recipient
+ expected.bcc = @recipient
expected.date = Time.local 2004, 12, 12
created = TestMailer.utf8_body @recipient
@@ -789,18 +785,21 @@ EOF
def test_multiple_utf8_recipients
@recipient = ["\"Foo áëô îü\" <extended@example.net>", "\"Example Recipient\" <me@example.com>"]
- expected = new_mail "utf-8"
- expected.to = quote_address_if_necessary @recipient, "utf-8"
- expected.subject = "testing utf-8 body"
+ expected = new_mail "UTF-8"
+ expected.to = @recipient
+ expected.subject = "testing UTF-8 body"
expected.body = "åœö blah"
- expected.from = quote_address_if_necessary @recipient.first, "utf-8"
- expected.cc = quote_address_if_necessary @recipient, "utf-8"
- expected.bcc = quote_address_if_necessary @recipient, "utf-8"
+ expected.from = @recipient.first
+ expected.cc = @recipient
+ expected.bcc = @recipient
expected.date = Time.local 2004, 12, 12
created = TestMailer.utf8_body @recipient
- assert_match(/\nFrom: =\?utf-8\?Q\?Foo_.*?\?= <extended@example.net>\r/, created.encoded)
- assert_match(/\nTo: =\?utf-8\?Q\?Foo_.*?\?= <extended@example.net>, \r\n\tExample Recipient <me/, created.encoded)
+ from_regexp = Regexp.escape('From: Foo =?UTF-8?B?w6HDq8O0?= =?UTF-8?B?IMOuw7w=?=')
+ assert_match(/#{from_regexp}/m, created.encoded)
+
+ to_regexp = Regexp.escape("To: =?UTF-8?B?Rm9vIMOhw6vDtCDDrsO8?= <extended@example.net>")
+ assert_match(/#{to_regexp}/m, created.encoded)
end
def test_receive_decodes_base64_encoded_mail
@@ -864,12 +863,20 @@ EOF
def test_multipart_with_utf8_subject
mail = TestMailer.multipart_with_utf8_subject(@recipient)
- assert_match(/\nSubject: =\?utf-8\?Q\?Foo_.*?\?=/, mail.encoded)
+ regex = Regexp.escape('Subject: Foo =?UTF-8?Q?=C3=A1=C3=AB=C3=B4=?= =?UTF-8?Q?_=C3=AE=C3=BC=?=')
+ assert_match(/#{regex}/, mail.encoded)
+ string = "Foo áëô îü"
+ string.force_encoding('UTF-8') if string.respond_to?(:force_encoding)
+ assert_match(string, mail.subject)
end
def test_implicitly_multipart_with_utf8
mail = TestMailer.implicitly_multipart_with_utf8
- assert_match(/\nSubject: =\?utf-8\?Q\?Foo_.*?\?=/, mail.encoded)
+ regex = Regexp.escape('Subject: Foo =?UTF-8?Q?=C3=A1=C3=AB=C3=B4=?= =?UTF-8?Q?_=C3=AE=C3=BC=?=')
+ assert_match(/#{regex}/, mail.encoded)
+ string = "Foo áëô îü"
+ string.force_encoding('UTF-8') if string.respond_to?(:force_encoding)
+ assert_match(string, mail.subject)
end
def test_explicitly_multipart_messages
@@ -909,11 +916,11 @@ EOF
assert_equal "1.0", mail.mime_version.to_s
assert_equal "multipart/alternative", mail.mime_type
assert_equal "text/plain", mail.parts[0].mime_type
- assert_equal "utf-8", mail.parts[0].charset
+ assert_equal "UTF-8", mail.parts[0].charset
assert_equal "text/html", mail.parts[1].mime_type
- assert_equal "utf-8", mail.parts[1].charset
+ assert_equal "UTF-8", mail.parts[1].charset
assert_equal "application/x-yaml", mail.parts[2].mime_type
- assert_equal "utf-8", mail.parts[2].charset
+ assert_equal "UTF-8", mail.parts[2].charset
end
def test_implicitly_multipart_messages_with_custom_order
@@ -1044,13 +1051,13 @@ EOF
mail = FunkyPathMailer.multipart_with_template_path_with_dots(@recipient)
assert_equal 2, mail.parts.length
assert "text/plain", mail.parts[1].mime_type
- assert "utf-8", mail.parts[1].charset
+ assert "UTF-8", mail.parts[1].charset
end
def test_custom_content_type_attributes
mail = TestMailer.custom_content_type_attributes
assert_match %r{format=flowed}, mail.content_type
- assert_match %r{charset=utf-8}, mail.content_type
+ assert_match %r{charset=UTF-8}, mail.content_type
end
def test_return_path_with_create
diff --git a/actionmailer/test/old_base/url_test.rb b/actionmailer/test/old_base/url_test.rb
index 17b383cc2a..6895fb230e 100644
--- a/actionmailer/test/old_base/url_test.rb
+++ b/actionmailer/test/old_base/url_test.rb
@@ -29,13 +29,12 @@ class UrlTestMailer < ActionMailer::Base
end
class ActionMailerUrlTest < Test::Unit::TestCase
- include ActionMailer::Quoting
- def encode( text, charset="utf-8" )
+ def encode( text, charset="UTF-8" )
quoted_printable( text, charset )
end
- def new_mail( charset="utf-8" )
+ def new_mail( charset="UTF-8" )
mail = Mail.new
mail.mime_version = "1.0"
if charset
diff --git a/actionmailer/test/quoting_test.rb b/actionmailer/test/quoting_test.rb
deleted file mode 100644
index 7640f4b086..0000000000
--- a/actionmailer/test/quoting_test.rb
+++ /dev/null
@@ -1,106 +0,0 @@
-# encoding: utf-8
-require 'abstract_unit'
-require 'tempfile'
-
-class QuotingTest < Test::Unit::TestCase
- # Move some tests from TMAIL here
- def test_unquote_quoted_printable
- a ="=?ISO-8859-1?Q?[166417]_Bekr=E6ftelse_fra_Rejsefeber?="
- b = Mail::Encodings.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 = Mail::Encodings.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 = Mail::Encodings.unquote_and_convert_to(a, 'utf-8')
- assert_equal "[166417]_Bekr=E6ftelse_fra_Rejsefeber", b
- end
-
- def test_unqoute_multiple
- a ="=?utf-8?q?Re=3A_=5B12=5D_=23137=3A_Inkonsistente_verwendung_von_=22Hin?==?utf-8?b?enVmw7xnZW4i?="
- b = Mail::Encodings.unquote_and_convert_to(a, 'utf-8')
- assert_equal "Re: [12] #137: Inkonsistente verwendung von \"Hinzuf\303\274gen\"", b
- end
-
- def test_unqoute_in_the_middle
- a ="Re: Photos =?ISO-8859-1?Q?Brosch=FCre_Rand?="
- b = Mail::Encodings.unquote_and_convert_to(a, 'utf-8')
- assert_equal "Re: Photos Brosch\303\274re Rand", b
- end
-
- def test_unqoute_iso
- a ="=?ISO-8859-1?Q?Brosch=FCre_Rand?="
- b = Mail::Encodings.unquote_and_convert_to(a, 'iso-8859-1')
- expected = "Brosch\374re Rand"
- expected.force_encoding 'iso-8859-1' if expected.respond_to?(:force_encoding)
- assert_equal expected, b
- end
-
- def test_quote_multibyte_chars
- original = "\303\246 \303\270 and \303\245"
- original.force_encoding('ASCII-8BIT') if original.respond_to?(:force_encoding)
-
- result = execute_in_sandbox(<<-CODE)
- $:.unshift(File.dirname(__FILE__) + "/../lib/")
- if RUBY_VERSION < '1.9'
- $KCODE = 'u'
- end
- require 'action_mailer/quoting'
- include ActionMailer::Quoting
- quoted_printable(#{original.inspect}, "UTF-8")
- CODE
-
- unquoted = Mail::Encodings.unquote_and_convert_to(result, nil)
-
- unquoted.force_encoding(Encoding::ASCII_8BIT) if unquoted.respond_to?(:force_encoding)
- original.force_encoding(Encoding::ASCII_8BIT) if original.respond_to?(:force_encoding)
-
- assert_equal unquoted, original
- end
-
-
- # test an email that has been created using \r\n newlines, instead of
- # \n newlines.
- def test_email_quoted_with_0d0a
- mail = Mail.new(IO.read("#{File.dirname(__FILE__)}/fixtures/raw_email_quoted_with_0d0a"))
- # CHANGED: subject returns an object now
- # assert_match %r{Elapsed time}, mail.body
- assert_match %r{Elapsed time}, mail.body.to_s
- end
-
- def test_email_with_partially_quoted_subject
- mail = Mail.new(IO.read("#{File.dirname(__FILE__)}/fixtures/raw_email_with_partially_quoted_subject"))
- # CHANGED: subject returns an object now
- # assert_equal "Re: Test: \"\346\274\242\345\255\227\" mid \"\346\274\242\345\255\227\" tail", mail.subject
- assert_equal "Re: Test: \"\346\274\242\345\255\227\" mid \"\346\274\242\345\255\227\" tail", mail.subject
- end
-
- private
- # This whole thing *could* be much simpler, but I don't think Tempfile,
- # popen and others exist on all platforms (like Windows).
- def execute_in_sandbox(code)
- test_name = "#{File.dirname(__FILE__)}/am-quoting-test.#{$$}.rb"
- res_name = "#{File.dirname(__FILE__)}/am-quoting-test.#{$$}.out"
-
- File.open(test_name, "w+") do |file|
- file.write(<<-CODE)
- block = Proc.new do
- #{code}
- end
- puts block.call
- CODE
- end
-
- system("ruby #{test_name} > #{res_name}") or raise "could not run test in sandbox"
- File.read(res_name).chomp
- ensure
- File.delete(test_name) rescue nil
- File.delete(res_name) rescue nil
- end
-end
diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb
index 3a38a91c28..440fb868c8 100644
--- a/actionmailer/test/test_helper_test.rb
+++ b/actionmailer/test/test_helper_test.rb
@@ -34,11 +34,7 @@ class TestHelperMailerTest < ActionMailer::TestCase
end
def test_charset_is_utf_8
- assert_equal "utf-8", charset
- end
-
- def test_encode
- assert_equal "=?utf-8?Q?=0Aasdf=0A?=", encode("\nasdf\n")
+ assert_equal "UTF-8", charset
end
def test_assert_emails
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 708683747b..388169d981 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,4 +1,11 @@
-*Rails 3.0.0 [beta 3] (pending)*
+*Rails 3.0.0 [beta 4/release candidate] (unreleased)*
+
+* Changed translate helper so that it doesn’t mark every translation as safe HTML. Only keys with a "_html" suffix and keys named "html" are considered to be safe HTML. All other translations are left untouched. [Craig Davey]
+
+
+*Rails 3.0.0 [beta 3] (April 13th, 2010)*
+
+* New option :as added to form_for allows to change the object name. The old <% form_for :client, @post %> becomes <% form_for @post, :as => :client %> [spastorino]
* Removed verify method in controllers. [JV]
It's now available as a plugin at http://github.com/rails/verification
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 98c8c5fa67..d2db366140 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -24,8 +24,7 @@ module AbstractController
end
def locale=(value)
- @i18n_config.locale = value
- @lookup_context.update_details(:locale => @i18n_config.locale)
+ @lookup_context.locale = value
end
end
diff --git a/actionpack/lib/action_controller/deprecated/base.rb b/actionpack/lib/action_controller/deprecated/base.rb
index 5d9cfb153a..b817bf42bc 100644
--- a/actionpack/lib/action_controller/deprecated/base.rb
+++ b/actionpack/lib/action_controller/deprecated/base.rb
@@ -148,6 +148,7 @@ module ActionController
deprecated_config_writer :session_store
deprecated_config_writer :session_options
+ deprecated_config_accessor :perform_caching
deprecated_config_accessor :relative_url_root, "relative_url_root is ineffective. Please stop using it"
deprecated_config_accessor :assets_dir
deprecated_config_accessor :javascripts_dir
@@ -156,4 +157,4 @@ module ActionController
delegate :consider_all_requests_local, :consider_all_requests_local=,
:allow_concurrency, :allow_concurrency=, :to => :"self.class"
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 7035e360ec..53585740ce 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -55,6 +55,14 @@ module ActionDispatch
path = args.first
end
+ if @scope[:module] && options[:to]
+ if options[:to].to_s.include?("#")
+ options[:to] = "#{@scope[:module]}/#{options[:to]}"
+ elsif @scope[:controller].nil?
+ options[:to] = "#{@scope[:module]}##{options[:to]}"
+ end
+ end
+
path = normalize_path(path)
if using_match_shorthand?(path, options)
@@ -116,11 +124,16 @@ module ActionDispatch
controller, action = to.split('#')
{ :controller => controller, :action => action }
when Symbol
- { :action => to.to_s }.merge(default_controller ? { :controller => default_controller } : {})
+ { :action => to.to_s }
else
- default_controller ? { :controller => default_controller } : {}
+ {}
end
+ defaults[:controller] ||= default_controller
+
+ defaults.delete(:controller) if defaults[:controller].blank?
+ defaults.delete(:action) if defaults[:action].blank?
+
if defaults[:controller].blank? && segment_keys.exclude?("controller")
raise ArgumentError, "missing :controller"
end
@@ -167,7 +180,11 @@ module ActionDispatch
end
def default_controller
- @scope[:controller].to_s if @scope[:controller]
+ if @options[:controller]
+ @options[:controller].to_s
+ elsif @scope[:controller]
+ @scope[:controller].to_s
+ end
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 563d9ec319..8731ed0ef3 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -709,7 +709,7 @@ module ActionView
# You can enable or disable the asset tag timestamps cache.
# With the cache enabled, the asset tag helper methods will make fewer
- # expense file system calls. However this prevents you from modifying
+ # expensive file system calls. However this prevents you from modifying
# any asset files while the server is running.
#
# ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index a3453cc47a..d3604925e8 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -8,90 +8,91 @@ require 'active_support/core_ext/object/blank'
module ActionView
module Helpers
- # Form helpers are designed to make working with models much easier
- # compared to using just standard HTML elements by providing a set of
- # methods for creating forms based on your models. This helper generates
- # the HTML for forms, providing a method for each sort of input
- # (e.g., text, password, select, and so on). When the form is submitted
- # (i.e., when the user hits the submit button or <tt>form.submit</tt> is
- # called via JavaScript), the form inputs will be bundled into the
- # <tt>params</tt> object and passed back to the controller.
+ # Form helpers are designed to make working with resources much easier
+ # compared to using vanilla HTML.
#
- # There are two types of form helpers: those that specifically work with
- # model attributes and those that don't. This helper deals with those that
- # work with model attributes; to see an example of form helpers that don't
- # work with model attributes, check the ActionView::Helpers::FormTagHelper
- # documentation.
+ # Forms for models are created with +form_for+. That method yields a form
+ # builder that knows the model the form is about. The form builder is thus
+ # able to generate default values for input fields that correspond to model
+ # attributes, and also convenient names, IDs, endpoints, etc.
#
- # The core method of this helper, form_for, gives you the ability to create
- # a form for a model instance; for example, let's say that you have a model
- # <tt>Person</tt> and want to create a new instance of it:
+ # Conventions in the generated field names allow controllers to receive form
+ # data nicely structured in +params+ with no effort on your side.
#
- # # Note: a @person variable will have been created in the controller.
- # # For example: @person = Person.new
- # <%= form_for @person do |f| %>
- # <%= f.text_field :first_name %>
- # <%= f.text_field :last_name %>
- # <%= submit_tag 'Create' %>
- # <% end %>
+ # For example, to create a new person you typically set up a new instance of
+ # +Person+ in the <tt>PeopleController#new</tt> action, <tt>@person</tt>, and
+ # pass it to +form_for+:
#
- # The HTML generated for this would be:
+ # <%= form_for @person do |f| %>
+ # <%= f.label :first_name %>:
+ # <%= f.text_field :first_name %><br />
#
- # <form action="/persons/create" method="post">
- # <input id="person_first_name" name="person[first_name]" size="30" type="text" />
- # <input id="person_last_name" name="person[last_name]" size="30" type="text" />
- # <input name="commit" type="submit" value="Create" />
- # </form>
+ # <%= f.label :last_name %>:
+ # <%= f.text_field :last_name %><br />
#
- # If you are using a partial for your form fields, you can use this shortcut:
+ # <%= f.submit %>
+ # <% end %>
#
- # <%= form_for :person, @person do |form| %>
- # <%= render :partial => f %>
- # <%= submit_tag 'Create' %>
- # <% end %>
+ # The HTML generated for this would be (modulus formatting):
#
- # This example will render the <tt>people/_form</tt> partial, setting a
- # local variable called <tt>form</tt> which references the yielded
- # FormBuilder. The <tt>params</tt> object created when this form is
- # submitted would look like:
+ # <form action="/people" class="new_person" id="new_person" method="post">
+ # <div style="margin:0;padding:0;display:inline">
+ # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
+ # </div>
+ # <label for="person_first_name">First name</label>:
+ # <input id="person_first_name" name="person[first_name]" size="30" type="text" /><br />
#
- # {"action"=>"create", "controller"=>"persons", "person"=>{"first_name"=>"William", "last_name"=>"Smith"}}
+ # <label for="person_last_name">Last name</label>:
+ # <input id="person_last_name" name="person[last_name]" size="30" type="text" /><br />
#
- # The params hash has a nested <tt>person</tt> value, which can therefore
- # be accessed with <tt>params[:person]</tt> in the controller. If were
- # editing/updating an instance (e.g., <tt>Person.find(1)</tt> rather than
- # <tt>Person.new</tt> in the controller), the objects attribute values are
- # filled into the form (e.g., the <tt>person_first_name</tt> field would
- # have that person's first name in it).
+ # <input id="person_submit" name="commit" type="submit" value="Create Person" />
+ # </form>
#
- # If the object name contains square brackets the id for the object will be
- # inserted. For example:
+ # As you see, the HTML reflects knowledge about the resource in several spots,
+ # like the path the form should be submitted to, or the names of the input fields.
#
- # <%= text_field "person[]", "name" %>
+ # In particular, thanks to the conventions followed in the generated field names, the
+ # controller gets a nested hash <tt>params[:person]</tt> with the person attributes
+ # set in the form. That hash is ready to be passed to <tt>Person.create</tt>:
#
- # ...will generate the following ERb.
+ # if @person = Person.create(params[:person])
+ # # success
+ # else
+ # # error handling
+ # end
#
- # <input type="text" id="person_<%= @person.id %>_name" name="person[<%= @person.id %>][name]" value="<%= @person.name %>" />
+ # Interestingly, the exact same view code in the previous example can be used to edit
+ # a person. If <tt>@person</tt> is an existing record with name "John Smith" and ID 256,
+ # the code above as is would yield instead:
#
- # If the helper is being used to generate a repetitive sequence of similar
- # form elements, for example in a partial used by
- # <tt>render_collection_of_partials</tt>, the <tt>index</tt> option may
- # come in handy. Example:
+ # <form action="/people/256" class="edit_person" id="edit_person_256" method="post">
+ # <div style="margin:0;padding:0;display:inline">
+ # <input name="_method" type="hidden" value="put" />
+ # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
+ # </div>
+ # <label for="person_first_name">First name</label>:
+ # <input id="person_first_name" name="person[first_name]" size="30" type="text" value="John" /><br />
#
- # <%= text_field "person", "name", "index" => 1 %>
+ # <label for="person_last_name">Last name</label>:
+ # <input id="person_last_name" name="person[last_name]" size="30" type="text" value="Smith" /><br />
#
- # ...becomes...
+ # <input id="person_submit" name="commit" type="submit" value="Update Person" />
+ # </form>
#
- # <input type="text" id="person_1_name" name="person[1][name]" value="<%= @person.name %>" />
+ # Note that the endpoint, default values, and submit button label are tailored for <tt>@person</tt>.
+ # That works that way because the involved helpers know whether the resource is a new record or not,
+ # and generate HTML accordingly.
#
- # An <tt>index</tt> option may also be passed to <tt>form_for</tt> and
- # <tt>fields_for</tt>. This automatically applies the <tt>index</tt> to
- # all the nested fields.
+ # The controller would receive the form data again in <tt>params[:person]</tt>, ready to be
+ # passed to <tt>Person#update_attributes</tt>:
#
- # There are also methods for helping to build form tags in
- # link:classes/ActionView/Helpers/FormOptionsHelper.html,
- # link:classes/ActionView/Helpers/DateHelper.html, and
- # link:classes/ActionView/Helpers/ActiveRecordHelper.html
+ # if @person.update_attributes(params[:person])
+ # # success
+ # else
+ # # error handling
+ # end
+ #
+ # That's how you tipically work with resources.
module FormHelper
extend ActiveSupport::Concern
@@ -129,9 +130,8 @@ module ActionView
# Admin? : <%= f.check_box :admin %><br />
# <% end %>
#
- # There, the first argument is a symbol or string with the name of the
- # object the form is about, and also the name of the instance variable
- # the object is stored in.
+ # There, the argument is a symbol or string with the name of the
+ # object the form is about.
#
# The form builder acts as a regular form helper that somehow carries the
# model. Thus, the idea is that
@@ -142,26 +142,7 @@ module ActionView
#
# <%= text_field :person, :first_name %>
#
- # If the instance variable is not <tt>@person</tt> you can pass the actual
- # record as the second argument:
- #
- # <%= form_for :person, person do |f| %>
- # ...
- # <% end %>
- #
- # In that case you can think
- #
- # <%= f.text_field :first_name %>
- #
- # gets expanded to
- #
- # <%= text_field :person, :first_name, :object => person %>
- #
- # You can even display error messages of the wrapped model this way:
- #
- # <%= f.error_messages %>
- #
- # In any of its variants, the rightmost argument to +form_for+ is an
+ # The rightmost argument to +form_for+ is an
# optional hash of options:
#
# * <tt>:url</tt> - The URL the form is submitted to. It takes the same
@@ -177,7 +158,7 @@ module ActionView
# possible to use both the stand-alone FormHelper methods and methods
# from FormTagHelper. For example:
#
- # <%= form_for :person, @person do |f| %>
+ # <%= form_for @person do |f| %>
# First name: <%= f.text_field :first_name %>
# Last name : <%= f.text_field :last_name %>
# Biography : <%= text_area :person, :biography %>
@@ -203,7 +184,7 @@ module ActionView
#
# is equivalent to something like:
#
- # <%= form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
+ # <%= form_for @post, :as => :post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
# ...
# <% end %>
#
@@ -213,9 +194,9 @@ module ActionView
# ...
# <% end %>
#
- # expands to
+ # is equivalent to something like:
#
- # <%= form_for :post, Post.new, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
+ # <%= form_for @post, :as => :post, :url => post_path(@post), :html => { :class => "new_post", :id => "new_post" } do |f| %>
# ...
# <% end %>
#
@@ -225,6 +206,13 @@ module ActionView
# ...
# <% end %>
#
+ # If you have an object that needs to be represented as a different
+ # parameter, like a Client that acts as a Person:
+ #
+ # <%= form_for(@post, :as => :client do |f| %>
+ # ...
+ # <% end %>
+ #
# And for namespaced routes, like +admin_post_url+:
#
# <%= form_for([:admin, @post]) do |f| %>
@@ -245,13 +233,13 @@ module ActionView
#
# Example:
#
- # <%= form_for(:post, @post, :remote => true, :html => { :id => 'create-post', :method => :put }) do |f| %>
+ # <%= form_for(@post, :remote => true) do |f| %>
# ...
# <% end %>
#
# The HTML generated for this would be:
#
- # <form action='http://www.example.com' id='create-post' method='post' data-remote='true'>
+ # <form action='http://www.example.com' method='post' data-remote='true'>
# <div style='margin:0;padding:0;display:inline'>
# <input name='_method' type='hidden' value='put' />
# </div>
@@ -265,7 +253,7 @@ module ActionView
# custom builder. For example, let's say you made a helper to
# automatically add labels to form inputs.
#
- # <%= form_for :person, @person, :url => { :action => "create" }, :builder => LabellingFormBuilder do |f| %>
+ # <%= form_for @person, :url => { :action => "create" }, :builder => LabellingFormBuilder do |f| %>
# <%= f.text_field :first_name %>
# <%= f.text_field :last_name %>
# <%= text_area :person, :biography %>
@@ -539,10 +527,7 @@ module ActionView
end
builder = options[:builder] || ActionView::Base.default_form_builder
-
- with_output_buffer do
- yield builder.new(object_name, object, self, options, block)
- end
+ capture(builder.new(object_name, object, self, options, block), &block)
end
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index 457944dbb6..89c1b4a275 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -3,17 +3,28 @@ require 'action_view/helpers/tag_helper'
module ActionView
module Helpers
module TranslationHelper
- # Delegates to I18n#translate but also performs two additional functions. First, it'll catch MissingTranslationData exceptions
+ # Delegates to I18n#translate but also performs three additional functions. First, it'll catch MissingTranslationData exceptions
# and turn them into inline spans that contains the missing key, such that you can see in a view what is missing where.
#
# Second, it'll scope the key by the current partial if the key starts with a period. So if you call translate(".foo") from the
# people/index.html.erb template, you'll actually be calling I18n.translate("people.index.foo"). This makes it less repetitive
# to translate many keys within the same partials and gives you a simple framework for scoping them consistently. If you don't
# prepend the key with a period, nothing is converted.
+ #
+ # Third, it’ll mark the translation as safe HTML if the key has the suffix "_html" or the last element of the key is the word
+ # "html". For example, calling translate("footer_html") or translate("footer.html") will return a safe HTML string that won’t
+ # be escaped by other HTML helper methods. This naming convention helps to identify translations that include HTML tags so that
+ # you know what kind of output to expect when you call translate in a template.
+
def translate(key, options = {})
options[:raise] = true
translation = I18n.translate(scope_key_by_partial(key), options)
- (translation.respond_to?(:join) ? translation.join : translation).html_safe
+ translation = (translation.respond_to?(:join) ? translation.join : translation)
+ if html_safe_translation_key? key
+ translation.html_safe
+ else
+ translation
+ end
rescue I18n::MissingTranslationData => e
keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
content_tag('span', keys.join(', '), :class => 'translation_missing')
@@ -27,7 +38,7 @@ module ActionView
alias :l :localize
private
-
+
def scope_key_by_partial(key)
strkey = key.respond_to?(:join) ? key.join : key.to_s
if strkey.first == "."
@@ -40,6 +51,15 @@ module ActionView
key
end
end
+
+ def html_safe_translation_key?(key)
+ last_key = if key.is_a? Array
+ key.last
+ else
+ key.to_s.split('.').last
+ end
+ (last_key == "html") || (last_key.ends_with? "_html")
+ end
end
end
end
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index 6e61d85dcc..7021342b4a 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -58,7 +58,7 @@ module ActionView
def initialize(view_paths, details = {})
@details, @details_key = { :handlers => default_handlers }, nil
- @frozen_formats = false
+ @frozen_formats, @skip_default_locale = false, false
self.view_paths = view_paths
self.update_details(details, true)
end
@@ -147,7 +147,13 @@ module ActionView
super(value)
end
- # Overload locale to return a symbol instead of array
+ # Do not use the default locale on template lookup.
+ def skip_default_locale!
+ @skip_default_locale = true
+ self.locale = nil
+ end
+
+ # Overload locale to return a symbol instead of array.
def locale
@details[:locale].first
end
@@ -160,7 +166,8 @@ module ActionView
config = I18n.config.respond_to?(:i18n_config) ? I18n.config.i18n_config : I18n.config
config.locale = value
end
- super(_locale_defaults)
+
+ super(@skip_default_locale ? I18n.locale : _locale_defaults)
end
# Update the details keys by merging the given hash into the current
diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb
index 4d687c1ec6..e1c1128753 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -209,7 +209,7 @@ class LayoutStatusIsRenderedTest < ActionController::TestCase
end
end
-unless RUBY_PLATFORM =~ /(:?mswin|mingw|bccwin)/
+unless RUBY_PLATFORM =~ /mswin|mingw/
class LayoutSymlinkedTest < LayoutTest
layout "symlinked/symlinked_layout"
end
diff --git a/actionpack/test/controller/new_base/content_type_test.rb b/actionpack/test/controller/new_base/content_type_test.rb
index 0ff5552b08..700b71a7f3 100644
--- a/actionpack/test/controller/new_base/content_type_test.rb
+++ b/actionpack/test/controller/new_base/content_type_test.rb
@@ -23,7 +23,9 @@ module ContentType
"content_type/implied/i_am_html_erb.html.erb" => "Hello world!",
"content_type/implied/i_am_xml_erb.xml.erb" => "<xml>Hello world!</xml>",
"content_type/implied/i_am_html_builder.html.builder" => "xml.p 'Hello'",
- "content_type/implied/i_am_xml_builder.xml.builder" => "xml.awesome 'Hello'"
+ "content_type/implied/i_am_xml_builder.xml.builder" => "xml.awesome 'Hello'",
+ "content_type/implied/i_am_rjs_in_html.html.erb" => "<%= render 'i_am_rjs_partial' %>",
+ "content_type/implied/_i_am_rjs_partial.js.rjs" => ""
)]
def i_am_html_erb() end
@@ -91,6 +93,12 @@ module ContentType
assert_header "Content-Type", "application/xml; charset=utf-8"
end
+
+ test "sets Content-Type as text/html when rendering *.html.erb with a RJS partial" do
+ get "/content_type/implied/i_am_rjs_in_html"
+
+ assert_header "Content-Type", "text/html; charset=utf-8"
+ end
end
class ExplicitCharsetTest < Rack::TestCase
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index d38c48bfd4..8940990712 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -52,6 +52,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
match 'global/:action'
end
+ match "/local/:action", :controller => "local"
+
constraints(:ip => /192\.168\.1\.\d\d\d/) do
get 'admin' => "queenbee#index"
end
@@ -140,7 +142,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
namespace :account do
match 'shorthand'
- match 'description', :to => "account#description", :as => "description"
+ match 'description', :to => "description", :as => "description"
resource :subscription, :credit, :credit_card
root :to => "account#index"
@@ -195,6 +197,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resource :me
match '/' => 'mes#index'
end
+
+ match "whatever/:controller(/:action(/:id))"
end
end
@@ -415,6 +419,13 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_local
+ with_test_routes do
+ get '/local/dashboard'
+ assert_equal 'local#dashboard', @response.body
+ end
+ end
+
def test_projects
with_test_routes do
get '/projects'
@@ -864,7 +875,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
with_test_routes do
assert_equal '/account', account_root_path
get '/account'
- assert_equal 'account#index', @response.body
+ assert_equal 'account/account#index', @response.body
end
end
@@ -970,6 +981,16 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_url_generator_for_generic_route
+ with_test_routes do
+ get 'whatever/foo/bar'
+ assert_equal 'foo#bar', @response.body
+
+ assert_equal 'http://www.example.com/whatever/foo/bar/1',
+ url_for(:controller => "foo", :action => "bar", :id => 1)
+ end
+ end
+
private
def with_test_routes
yield
diff --git a/actionpack/test/fixtures/test/array_translation.erb b/actionpack/test/fixtures/test/array_translation.erb
index 12c0763313..def3a1a0c1 100644
--- a/actionpack/test/fixtures/test/array_translation.erb
+++ b/actionpack/test/fixtures/test/array_translation.erb
@@ -1 +1 @@
-<%= t(['foo', 'bar']) %> \ No newline at end of file
+<%= t(['foo', 'bar', 'html']) %> \ No newline at end of file
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 28a13b07be..47ac911540 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -1518,6 +1518,11 @@ class FormHelperTest < ActionView::TestCase
assert_equal expected, output_buffer
end
+ def test_fields_for_returns_block_result
+ output = fields_for(Post.new) { |f| "fields" }
+ assert_equal "fields", output
+ end
+
protected
def comments_path(post)
"/posts/#{post.id}/comments"
diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb
index a21a1a68e4..14e81fc9dc 100644
--- a/actionpack/test/template/number_helper_test.rb
+++ b/actionpack/test/template/number_helper_test.rb
@@ -188,6 +188,7 @@ class NumberHelperTest < ActionView::TestCase
assert_equal '10 KB', number_to_human_size(kilobytes(10.000), :precision => 4)
assert_equal '1 TB', number_to_human_size(1234567890123, :precision => 1)
assert_equal '500 MB', number_to_human_size(524288000, :precision=>3)
+ assert_equal '10 MB', number_to_human_size(9961472, :precision=>0)
assert_equal '40 KB', number_to_human_size(41010, :precision => 1)
assert_equal '40 KB', number_to_human_size(41100, :precision => 2)
assert_equal '1.0 KB', number_to_human_size(kilobytes(1.0123), :precision => 2, :strip_insignificant_zeros => false)
diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb
index 6782bf06d4..b382b5eb22 100644
--- a/actionpack/test/template/translation_helper_test.rb
+++ b/actionpack/test/template/translation_helper_test.rb
@@ -25,7 +25,7 @@ class TranslationHelperTest < ActiveSupport::TestCase
def test_translation_of_an_array_with_html
expected = '<a href="#">foo</a><a href="#">bar</a>'
- I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(['<a href="#">foo</a>', '<a href="#">bar</a>'])
+ I18n.expects(:translate).with(["foo", "bar", "html"], :raise => true).returns(['<a href="#">foo</a>', '<a href="#">bar</a>'])
@view = ActionView::Base.new(ActionController::Base.view_paths, {})
assert_equal expected, @view.render(:file => "test/array_translation")
end
@@ -47,4 +47,19 @@ class TranslationHelperTest < ActiveSupport::TestCase
@view = ActionView::Base.new(ActionController::Base.view_paths, {})
assert_equal "foobar", @view.render(:file => "test/scoped_array_translation")
end
+
+ def test_translate_does_not_mark_plain_text_as_safe_html
+ I18n.expects(:translate).with("hello", :raise => true).returns("Hello World")
+ assert_equal false, translate("hello").html_safe?
+ end
+
+ def test_translate_marks_translations_named_html_as_safe_html
+ I18n.expects(:translate).with("html", :raise => true).returns("<a>Hello World</a>")
+ assert translate("html").html_safe?
+ end
+
+ def test_translate_marks_translations_with_a_html_suffix_as_safe_html
+ I18n.expects(:translate).with("hello_html", :raise => true).returns("<a>Hello World</a>")
+ assert translate("hello_html").html_safe?
+ end
end
diff --git a/activemodel/CHANGELOG b/activemodel/CHANGELOG
index 8c458b6091..74aec3bfd3 100644
--- a/activemodel/CHANGELOG
+++ b/activemodel/CHANGELOG
@@ -1,3 +1,8 @@
+*Rails 3.0.0 [beta 3] (April 13th, 2010)*
+
+* No changes
+
+
*Rails 3.0.0 [beta 2] (April 1st, 2010)*
* #new_record? and #destroyed? were removed from ActiveModel::Lint. Use
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index e0625c3dbb..8ecef6574f 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,4 +1,4 @@
-*Rails 3.0.0 [Beta 2] (pending)*
+*Rails 3.0.0 [beta 3] (April 13th, 2010)*
* Add Relation extensions. [Pratik Naik]
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 20a8754b7c..d94cc03938 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1,5 +1,6 @@
-require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/enumerable'
+require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/object/blank'
module ActiveRecord
@@ -1707,9 +1708,9 @@ module ActiveRecord
silence_warnings do
self.parent.const_set(extension_module_name, Module.new(&block_extension))
end
- Array(extensions).push("#{self.parent}::#{extension_module_name}".constantize)
+ Array.wrap(extensions).push("#{self.parent}::#{extension_module_name}".constantize)
else
- Array(extensions)
+ Array.wrap(extensions)
end
end
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index b808f8c306..0dfd966466 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -1,4 +1,5 @@
require 'set'
+require 'active_support/core_ext/array/wrap'
module ActiveRecord
module Associations
@@ -98,7 +99,7 @@ module ActiveRecord
if @target.is_a?(Array)
@target.to_ary
else
- Array(@target)
+ Array.wrap(@target)
end
end
alias_method :to_a, :to_ary
@@ -450,6 +451,16 @@ module ActiveRecord
records
end
+ def add_record_to_target_with_callbacks(record)
+ callback(:before_add, record)
+ yield(record) if block_given?
+ @target ||= [] unless loaded?
+ @target << record unless @reflection.options[:uniq] && @target.include?(record)
+ callback(:after_add, record)
+ set_inverse_instance(record, @owner)
+ record
+ end
+
private
def create_record(attrs)
attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
@@ -474,16 +485,6 @@ module ActiveRecord
end
end
- def add_record_to_target_with_callbacks(record)
- callback(:before_add, record)
- yield(record) if block_given?
- @target ||= [] unless loaded?
- @target << record unless @reflection.options[:uniq] && @target.include?(record)
- callback(:after_add, record)
- set_inverse_instance(record, @owner)
- record
- end
-
def remove_records(*records)
records = flatten_deeper(records)
records.each { |record| raise_on_type_mismatch(record) }
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index 4fb1df3ab9..b9d0fe3abe 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/wrap'
+
module ActiveRecord
module Associations
# This is the root class of all association proxies:
@@ -55,7 +57,7 @@ module ActiveRecord
@owner, @reflection = owner, reflection
@updated = false
reflection.check_validity!
- Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
+ Array.wrap(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
reset
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index d544c48a4c..fd24dcddc3 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -3,6 +3,7 @@ require 'set'
require 'active_support/benchmarkable'
require 'active_support/dependencies'
require 'active_support/time'
+require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/class/delegating_attributes'
require 'active_support/core_ext/class/inheritable_attributes'
@@ -341,15 +342,15 @@ module ActiveRecord #:nodoc:
#
# If you are organising your models within modules you can add a prefix to the models within a namespace by defining
# a singleton method in the parent module called table_name_prefix which returns your chosen prefix.
- cattr_accessor :table_name_prefix, :instance_writer => false
- @@table_name_prefix = ""
+ class_attribute :table_name_prefix, :instance_writer => false
+ self.table_name_prefix = ""
##
# :singleton-method:
# Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
# "people_basecamp"). By default, the suffix is the empty string.
- cattr_accessor :table_name_suffix, :instance_writer => false
- @@table_name_suffix = ""
+ class_attribute :table_name_suffix, :instance_writer => false
+ self.table_name_suffix = ""
##
# :singleton-method:
@@ -1079,16 +1080,6 @@ module ActiveRecord #:nodoc:
end
end
- # Nest the type name in the same module as this class.
- # Bar is "MyApp::Business::Bar" relative to MyApp::Business::Foo
- def type_name_with_module(type_name)
- if store_full_sti_class
- type_name
- else
- (/^::/ =~ type_name) ? type_name : "#{parent.name}::#{type_name}"
- end
- end
-
def construct_finder_arel(options = {}, scope = nil)
relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : unscoped.merge(options)
relation = scope.merge(relation) if scope
@@ -1284,11 +1275,14 @@ module ActiveRecord #:nodoc:
with_scope(method_scoping, :overwrite, &block)
end
- def subclasses #:nodoc:
+ # Returns a list of all subclasses of this class, meaning all descendants.
+ def subclasses
@@subclasses[self] ||= []
- @@subclasses[self] + extra = @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
+ @@subclasses[self] + @@subclasses[self].inject([]) {|list, subclass| list + subclass.subclasses }
end
+ public :subclasses
+
# Sets the default options for the model. The format of the
# <tt>options</tt> argument is the same as in find.
#
@@ -1311,13 +1305,26 @@ module ActiveRecord #:nodoc:
# Returns the class type of the record using the current module as a prefix. So descendants of
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
def compute_type(type_name)
- modularized_name = type_name_with_module(type_name)
- silence_warnings do
- begin
- class_eval(modularized_name, __FILE__)
- rescue NameError
- class_eval(type_name, __FILE__)
+ if type_name.match(/^::/)
+ # If the type is prefixed with a scope operator then we assume that
+ # the type_name is an absolute reference.
+ type_name.constantize
+ else
+ # Build a list of candidates to search for
+ candidates = []
+ name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
+ candidates << type_name
+
+ candidates.each do |candidate|
+ begin
+ constant = candidate.constantize
+ return constant if candidate == constant.to_s
+ rescue NameError
+ rescue ArgumentError
+ end
end
+
+ raise NameError, "uninitialized constant #{candidates.first}"
end
end
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index add5d99ca6..98c14e6eb0 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/wrap'
+
module ActiveRecord
# Callbacks are hooks into the lifecycle of an Active Record object that allow you to trigger logic
# before or after an alteration of the object state. This can be used to make sure that associated and
@@ -250,7 +252,7 @@ module ActiveRecord
def before_validation(*args, &block)
options = args.last
if options.is_a?(Hash) && options[:on]
- options[:if] = Array(options[:if])
+ options[:if] = Array.wrap(options[:if])
options[:if] << "@_on_validate == :#{options[:on]}"
end
set_callback(:validation, :before, *args, &block)
@@ -259,7 +261,7 @@ module ActiveRecord
def after_validation(*args, &block)
options = args.extract_options!
options[:prepend] = true
- options[:if] = Array(options[:if])
+ options[:if] = Array.wrap(options[:if])
options[:if] << "!halted && value != false"
options[:if] << "@_on_validate == :#{options[:on]}" if options[:on]
set_callback(:validation, :after, *(args << options), &block)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index e5d100b51b..3c560903f7 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -32,7 +32,6 @@ module ActiveRecord
# Enable the query cache within the block.
def cache
old, @query_cache_enabled = @query_cache_enabled, true
- @query_cache ||= {}
yield
ensure
clear_query_cache
@@ -54,7 +53,7 @@ module ActiveRecord
# the same SQL query and repeatedly return the same result each time, silently
# undermining the randomness you were expecting.
def clear_query_cache
- @query_cache.clear if @query_cache
+ @query_cache.clear
end
def select_all_with_query_cache(*args)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 6d4ab501fa..e8cba1bd41 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/wrap'
+
module ActiveRecord
module ConnectionAdapters # :nodoc:
module SchemaStatements
@@ -267,7 +269,7 @@ module ActiveRecord
# generates
# CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
def add_index(table_name, column_name, options = {})
- column_names = Array(column_name)
+ column_names = Array.wrap(column_name)
index_name = index_name(table_name, :column => column_names)
if Hash === options # legacy support, since this param was a string
@@ -297,7 +299,7 @@ module ActiveRecord
def index_name(table_name, options) #:nodoc:
if Hash === options # legacy support
if options[:column]
- "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
+ "index_#{table_name}_on_#{Array.wrap(options[:column]) * '_and_'}"
elsif options[:name]
options[:name]
else
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 6ffffc8654..578297029b 100755
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -41,6 +41,7 @@ module ActiveRecord
@connection, @logger = connection, logger
@runtime = 0
@query_cache_enabled = false
+ @query_cache = {}
end
# Returns the human-readable name of the adapter. Use mixed case - one
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 68ee88bba4..ceb1adc9e0 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -54,6 +54,12 @@ module ActiveRecord
super(name, self.class.extract_value_from_default(default), sql_type, null)
end
+ # :stopdoc:
+ class << self
+ attr_accessor :money_precision
+ end
+ # :startdoc:
+
private
def extract_limit(sql_type)
case sql_type
@@ -71,9 +77,11 @@ module ActiveRecord
# Extracts the precision from PostgreSQL-specific data types.
def extract_precision(sql_type)
- # Actual code is defined dynamically in PostgreSQLAdapter.connect
- # depending on the server specifics
- super
+ if sql_type == 'money'
+ self.class.money_precision
+ else
+ super
+ end
end
# Maps PostgreSQL-specific data types to logical Rails types.
@@ -83,18 +91,18 @@ module ActiveRecord
when /^(?:real|double precision)$/
:float
# Monetary types
- when /^money$/
+ when 'money'
:decimal
# Character types
when /^(?:character varying|bpchar)(?:\(\d+\))?$/
:string
# Binary data types
- when /^bytea$/
+ when 'bytea'
:binary
# Date/time types
when /^timestamp with(?:out)? time zone$/
:datetime
- when /^interval$/
+ when 'interval'
:string
# Geometric types
when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
@@ -106,16 +114,16 @@ module ActiveRecord
when /^bit(?: varying)?(?:\(\d+\))?$/
:string
# XML type
- when /^xml$/
+ when 'xml'
:xml
# Arrays
when /^\D+\[\]$/
:string
# Object identifier types
- when /^oid$/
+ when 'oid'
:integer
# UUID type
- when /^uuid$/
+ when 'uuid'
:string
# Small and big integer types
when /^(?:small|big)int$/
@@ -383,9 +391,9 @@ module ActiveRecord
def quote(value, column = nil) #:nodoc:
if value.kind_of?(String) && column && column.type == :binary
"#{quoted_string_prefix}'#{escape_bytea(value)}'"
- elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/
+ elsif value.kind_of?(String) && column && column.sql_type == 'xml'
"xml E'#{quote_string(value)}'"
- elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/
+ elsif value.kind_of?(Numeric) && column && column.sql_type == 'money'
# Not truly string input, so doesn't require (or allow) escape string syntax.
"'#{value.to_s}'"
elsif value.kind_of?(String) && column && column.sql_type =~ /^bit/
@@ -662,8 +670,30 @@ module ActiveRecord
def tables(name = nil)
query(<<-SQL, name).map { |row| row[0] }
SELECT tablename
+ FROM pg_tables
+ WHERE schemaname = ANY (current_schemas(false))
+ SQL
+ end
+
+ def table_exists?(name)
+ name = name.to_s
+ schema, table = name.split('.', 2)
+
+ unless table # A table was provided without a schema
+ table = schema
+ schema = nil
+ end
+
+ if name =~ /^"/ # Handle quoted table names
+ table = name
+ schema = nil
+ end
+
+ query(<<-SQL).first[0].to_i > 0
+ SELECT COUNT(*)
FROM pg_tables
- WHERE schemaname = ANY (current_schemas(false))
+ WHERE tablename = '#{table.gsub(/(^"|"$)/,'')}'
+ #{schema ? "AND schemaname = '#{schema}'" : ''}
SQL
end
@@ -925,7 +955,7 @@ module ActiveRecord
# Construct a clean list of column names from the ORDER BY clause, removing
# any ASC/DESC modifiers
order_columns = order_by.split(',').collect { |s| s.split.first }
- order_columns.delete_if &:blank?
+ order_columns.delete_if(&:blank?)
order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
# Return a DISTINCT ON() clause that's distinct on the columns we want but includes
@@ -989,17 +1019,8 @@ module ActiveRecord
# Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
# PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
# should know about this but can't detect it there, so deal with it here.
- money_precision = (postgresql_version >= 80300) ? 19 : 10
- PostgreSQLColumn.module_eval(<<-end_eval)
- def extract_precision(sql_type) # def extract_precision(sql_type)
- if sql_type =~ /^money$/ # if sql_type =~ /^money$/
- #{money_precision} # 19
- else # else
- super # super
- end # end
- end # end
- end_eval
-
+ PostgreSQLColumn.money_precision =
+ (postgresql_version >= 80300) ? 19 : 10
configure_connection
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index ee2d420194..70a460d41d 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -348,14 +348,24 @@ module ActiveRecord
attributes_collection = attributes_collection.sort_by { |index, _| index.to_i }.map { |_, attributes| attributes }
end
+ association = send(association_name)
+
+ existing_records = if association.loaded?
+ association.to_a
+ else
+ attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
+ attribute_ids.present? ? association.all(:conditions => {:id => attribute_ids}) : []
+ end
+
attributes_collection.each do |attributes|
attributes = attributes.with_indifferent_access
if attributes['id'].blank?
unless reject_new_record?(association_name, attributes)
- send(association_name).build(attributes.except(*UNASSIGNABLE_KEYS))
+ association.build(attributes.except(*UNASSIGNABLE_KEYS))
end
- elsif existing_record = send(association_name).detect { |record| record.id.to_s == attributes['id'].to_s }
+ elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
+ association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
else
raise_nested_attributes_record_not_found(association_name, attributes['id'])
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index b5e8b7570a..09332418d5 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1,3 +1,4 @@
+require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
module ActiveRecord
@@ -9,7 +10,7 @@ module ActiveRecord
attr_accessor :"#{query_method}_values"
next if [:where, :having].include?(query_method)
- class_eval <<-CEVAL
+ class_eval <<-CEVAL, __FILE__
def #{query_method}(*args, &block)
new_relation = clone
new_relation.send(:apply_modules, Module.new(&block)) if block_given?
@@ -21,12 +22,12 @@ module ActiveRecord
end
[:where, :having].each do |query_method|
- class_eval <<-CEVAL
+ class_eval <<-CEVAL, __FILE__
def #{query_method}(*args, &block)
new_relation = clone
new_relation.send(:apply_modules, Module.new(&block)) if block_given?
value = build_where(*args)
- new_relation.#{query_method}_values += [*value] if value.present?
+ new_relation.#{query_method}_values += Array.wrap(value) if value.present?
new_relation
end
CEVAL
@@ -35,7 +36,7 @@ module ActiveRecord
ActiveRecord::Relation::SINGLE_VALUE_METHODS.each do |query_method|
attr_accessor :"#{query_method}_value"
- class_eval <<-CEVAL
+ class_eval <<-CEVAL, __FILE__
def #{query_method}(value = true, &block)
new_relation = clone
new_relation.send(:apply_modules, Module.new(&block)) if block_given?
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index b19920741e..2e85959b1e 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -1,3 +1,4 @@
+require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/conversions'
module ActiveRecord #:nodoc:
@@ -186,7 +187,7 @@ module ActiveRecord #:nodoc:
end
def serializable_method_attributes
- Array(options[:methods]).inject([]) do |method_attributes, name|
+ Array.wrap(options[:methods]).inject([]) do |method_attributes, name|
method_attributes << MethodAttribute.new(name.to_s, @serializable) if @serializable.respond_to?(name.to_s)
method_attributes
end
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 4806fa0ecc..6283bdd0d6 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/wrap'
+
module ActiveRecord
module Validations
class UniquenessValidator < ActiveModel::EachValidator
@@ -19,7 +21,7 @@ module ActiveRecord
relation = table.where(sql, *params)
- Array(options[:scope]).each do |scope_item|
+ Array.wrap(options[:scope]).each do |scope_item|
scope_value = record.send(scope_item)
relation = relation.where(scope_item => scope_value)
end
diff --git a/activerecord/lib/rails/generators/active_record/model/model_generator.rb b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
index 3e72fbeca8..539c2517ee 100644
--- a/activerecord/lib/rails/generators/active_record/model/model_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/model/model_generator.rb
@@ -20,6 +20,11 @@ module ActiveRecord
template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
end
+ def create_module_file
+ return if class_path.empty?
+ template 'module.rb', File.join('app/models', "#{class_path.join('/')}.rb")
+ end
+
hook_for :test_framework
protected
diff --git a/activerecord/lib/rails/generators/active_record/model/templates/module.rb b/activerecord/lib/rails/generators/active_record/model/templates/module.rb
new file mode 100644
index 0000000000..bb4220f038
--- /dev/null
+++ b/activerecord/lib/rails/generators/active_record/model/templates/module.rb
@@ -0,0 +1,5 @@
+module <%= class_path.map(&:camelize).join('::') %>
+ def self.table_name_prefix
+ '<%= class_path.join('_') %>_'
+ end
+end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 8774ed58aa..1c3655b587 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -579,9 +579,9 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal(topics(:second).title, topics.first.title)
end
- def test_table_name_guesses
- classes = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
+ GUESSED_CLASSES = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
+ def test_table_name_guesses
assert_equal "topics", Topic.table_name
assert_equal "categories", Category.table_name
@@ -592,9 +592,13 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
assert_equal "categories", CreditCard::Brand.table_name
assert_equal "master_credit_cards", MasterCreditCard.table_name
+ ensure
+ GUESSED_CLASSES.each(&:reset_table_name)
+ end
+ def test_singular_table_name_guesses
ActiveRecord::Base.pluralize_table_names = false
- classes.each(&:reset_table_name)
+ GUESSED_CLASSES.each(&:reset_table_name)
assert_equal "category", Category.table_name
assert_equal "smarts", Smarts.table_name
@@ -604,10 +608,12 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
assert_equal "category", CreditCard::Brand.table_name
assert_equal "master_credit_card", MasterCreditCard.table_name
-
+ ensure
ActiveRecord::Base.pluralize_table_names = true
- classes.each(&:reset_table_name)
+ GUESSED_CLASSES.each(&:reset_table_name)
+ end
+ def test_table_name_guesses_with_prefixes_and_suffixes
ActiveRecord::Base.table_name_prefix = "test_"
Category.reset_table_name
assert_equal "test_categories", Category.table_name
@@ -620,8 +626,15 @@ class BasicsTest < ActiveRecord::TestCase
ActiveRecord::Base.table_name_suffix = ""
Category.reset_table_name
assert_equal "categories", Category.table_name
+ ensure
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
+ GUESSED_CLASSES.each(&:reset_table_name)
+ end
+ def test_singular_table_name_guesses_with_prefixes_and_suffixes
ActiveRecord::Base.pluralize_table_names = false
+
ActiveRecord::Base.table_name_prefix = "test_"
Category.reset_table_name
assert_equal "test_category", Category.table_name
@@ -634,9 +647,40 @@ class BasicsTest < ActiveRecord::TestCase
ActiveRecord::Base.table_name_suffix = ""
Category.reset_table_name
assert_equal "category", Category.table_name
-
+ ensure
ActiveRecord::Base.pluralize_table_names = true
- classes.each(&:reset_table_name)
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
+ GUESSED_CLASSES.each(&:reset_table_name)
+ end
+
+ def test_table_name_guesses_with_inherited_prefixes_and_suffixes
+ GUESSED_CLASSES.each(&:reset_table_name)
+
+ CreditCard.table_name_prefix = "test_"
+ CreditCard.reset_table_name
+ Category.reset_table_name
+ assert_equal "test_credit_cards", CreditCard.table_name
+ assert_equal "categories", Category.table_name
+ CreditCard.table_name_suffix = "_test"
+ CreditCard.reset_table_name
+ Category.reset_table_name
+ assert_equal "test_credit_cards_test", CreditCard.table_name
+ assert_equal "categories", Category.table_name
+ CreditCard.table_name_prefix = ""
+ CreditCard.reset_table_name
+ Category.reset_table_name
+ assert_equal "credit_cards_test", CreditCard.table_name
+ assert_equal "categories", Category.table_name
+ CreditCard.table_name_suffix = ""
+ CreditCard.reset_table_name
+ Category.reset_table_name
+ assert_equal "credit_cards", CreditCard.table_name
+ assert_equal "categories", Category.table_name
+ ensure
+ CreditCard.table_name_prefix = ""
+ CreditCard.table_name_suffix = ""
+ GUESSED_CLASSES.each(&:reset_table_name)
end
def test_destroy_all
@@ -2009,6 +2053,10 @@ class BasicsTest < ActiveRecord::TestCase
assert !SubStiPost.descends_from_active_record?
end
+ def test_base_subclasses_is_public_method
+ assert ActiveRecord::Base.public_methods.include?("subclasses")
+ end
+
def test_find_on_abstract_base_class_doesnt_use_type_condition
old_class = LooseDescendant
Object.send :remove_const, :LooseDescendant
@@ -2157,14 +2205,6 @@ class BasicsTest < ActiveRecord::TestCase
assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
end
- def test_type_name_with_module_should_handle_beginning
- ActiveRecord::Base.store_full_sti_class = false
- assert_equal 'ActiveRecord::Person', ActiveRecord::Base.send(:type_name_with_module, 'Person')
- assert_equal '::Person', ActiveRecord::Base.send(:type_name_with_module, '::Person')
- ensure
- ActiveRecord::Base.store_full_sti_class = true
- end
-
def test_to_param_should_return_string
assert_kind_of String, Client.find(:first).to_param
end
diff --git a/activerecord/test/cases/datatype_test_postgresql.rb b/activerecord/test/cases/datatype_test_postgresql.rb
index 9454b6e059..3c2d9fb7bd 100644
--- a/activerecord/test/cases/datatype_test_postgresql.rb
+++ b/activerecord/test/cases/datatype_test_postgresql.rb
@@ -97,7 +97,7 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
def test_money_values
assert_equal 567.89, @first_money.wealth
- assert_equal -567.89, @second_money.wealth
+ assert_equal(-567.89, @second_money.wealth)
end
def test_number_values
diff --git a/activerecord/test/cases/modules_test.rb b/activerecord/test/cases/modules_test.rb
index 7209966bf8..c924c3dfad 100644
--- a/activerecord/test/cases/modules_test.rb
+++ b/activerecord/test/cases/modules_test.rb
@@ -1,8 +1,9 @@
require "cases/helper"
require 'models/company_in_module'
+require 'models/shop'
class ModulesTest < ActiveRecord::TestCase
- fixtures :accounts, :companies, :projects, :developers
+ fixtures :accounts, :companies, :projects, :developers, :collections, :products, :variants
def setup
# need to make sure Object::Firm and Object::Client are not defined,
@@ -110,4 +111,34 @@ class ModulesTest < ActiveRecord::TestCase
ActiveRecord::Base.table_name_prefix = ''
classes.each(&:reset_table_name)
end
+
+ def test_compute_type_can_infer_class_name_of_sibling_inside_module
+ old = ActiveRecord::Base.store_full_sti_class
+ ActiveRecord::Base.store_full_sti_class = true
+ assert_equal MyApplication::Business::Firm, MyApplication::Business::Client.send(:compute_type, "Firm")
+ ensure
+ ActiveRecord::Base.store_full_sti_class = old
+ end
+
+ def test_nested_models_should_not_raise_exception_when_using_delete_all_dependency_on_association
+ old = ActiveRecord::Base.store_full_sti_class
+ ActiveRecord::Base.store_full_sti_class = true
+
+ collection = Shop::Collection.find(:first)
+ assert !collection.products.empty?, "Collection should have products"
+ assert_nothing_raised { collection.destroy }
+ ensure
+ ActiveRecord::Base.store_full_sti_class = old
+ end
+
+ def test_nested_models_should_not_raise_exception_when_using_nullify_dependency_on_association
+ old = ActiveRecord::Base.store_full_sti_class
+ ActiveRecord::Base.store_full_sti_class = true
+
+ product = Shop::Product.find(:first)
+ assert !product.variants.empty?, "Product should have variants"
+ assert_nothing_raised { product.destroy }
+ ensure
+ ActiveRecord::Base.store_full_sti_class = old
+ end
end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 7ca9c416cb..eae8ae7e39 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -453,6 +453,16 @@ module NestedAttributesOnACollectionAssociationTests
assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
end
+ def test_should_not_load_association_when_updating_existing_records
+ @pirate.reload
+ @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
+ assert ! @pirate.send(@association_name).loaded?
+
+ @pirate.save
+ assert ! @pirate.send(@association_name).loaded?
+ assert_equal 'Grace OMalley', @child_1.reload.name
+ end
+
def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
@child_1.stubs(:id).returns('ABC1X')
@child_2.stubs(:id).returns('ABC2X')
@@ -507,7 +517,7 @@ module NestedAttributesOnACollectionAssociationTests
def test_should_ignore_new_associated_records_if_a_reject_if_proc_returns_false
@alternate_params[association_getter]['baz'] = {}
- assert_no_difference("@pirate.send(@association_name).length") do
+ assert_no_difference("@pirate.send(@association_name).count") do
@pirate.attributes = @alternate_params
end
end
diff --git a/activerecord/test/cases/schema_test_postgresql.rb b/activerecord/test/cases/schema_test_postgresql.rb
index a294848fa3..3ed73786a7 100644
--- a/activerecord/test/cases/schema_test_postgresql.rb
+++ b/activerecord/test/cases/schema_test_postgresql.rb
@@ -52,6 +52,21 @@ class SchemaTest < ActiveRecord::TestCase
@connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE"
end
+ def test_table_exists?
+ [Thing1, Thing2, Thing3, Thing4].each do |klass|
+ name = klass.table_name
+ assert @connection.table_exists?(name), "'#{name}' table should exist"
+ end
+ end
+
+ def test_table_exists_wrong_schema
+ assert(!@connection.table_exists?("foo.things"), "table should not exist")
+ end
+
+ def test_table_exists_quoted_table
+ assert(@connection.table_exists?('"things.table"'), "table should exist")
+ end
+
def test_with_schema_prefixed_table_name
assert_nothing_raised do
assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}")
diff --git a/activerecord/test/fixtures/collections.yml b/activerecord/test/fixtures/collections.yml
new file mode 100644
index 0000000000..ad0fd26554
--- /dev/null
+++ b/activerecord/test/fixtures/collections.yml
@@ -0,0 +1,3 @@
+collection_1:
+ id: 1
+ name: Collection
diff --git a/activerecord/test/fixtures/products.yml b/activerecord/test/fixtures/products.yml
new file mode 100644
index 0000000000..8a197fb038
--- /dev/null
+++ b/activerecord/test/fixtures/products.yml
@@ -0,0 +1,4 @@
+product_1:
+ id: 1
+ collection_id: 1
+ name: Product
diff --git a/activerecord/test/fixtures/variants.yml b/activerecord/test/fixtures/variants.yml
new file mode 100644
index 0000000000..06be30727b
--- /dev/null
+++ b/activerecord/test/fixtures/variants.yml
@@ -0,0 +1,4 @@
+variant_1:
+ id: 1
+ product_id: 1
+ name: Variant
diff --git a/activerecord/test/models/shop.rb b/activerecord/test/models/shop.rb
new file mode 100644
index 0000000000..b232185693
--- /dev/null
+++ b/activerecord/test/models/shop.rb
@@ -0,0 +1,12 @@
+module Shop
+ class Collection < ActiveRecord::Base
+ has_many :products, :dependent => :nullify
+ end
+
+ class Product < ActiveRecord::Base
+ has_many :variants, :dependent => :delete_all
+ end
+
+ class Variant < ActiveRecord::Base
+ end
+end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index bec4291457..7a0cf550e0 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -99,6 +99,10 @@ ActiveRecord::Schema.define do
t.string :name
end
+ create_table :collections, :force => true do |t|
+ t.string :name
+ end
+
create_table :colnametests, :force => true do |t|
t.integer :references, :null => false
end
@@ -394,6 +398,11 @@ ActiveRecord::Schema.define do
t.integer :price
end
+ create_table :products, :force => true do |t|
+ t.references :collection
+ t.string :name
+ end
+
create_table :projects, :force => true do |t|
t.string :name
t.string :type
@@ -499,6 +508,11 @@ ActiveRecord::Schema.define do
t.column :looter_type, :string
end
+ create_table :variants, :force => true do |t|
+ t.references :product
+ t.string :name
+ end
+
create_table :vertices, :force => true do |t|
t.column :label, :string
end
diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG
index 27bf99a8ee..91dccb9671 100644
--- a/activeresource/CHANGELOG
+++ b/activeresource/CHANGELOG
@@ -1,3 +1,8 @@
+*Rails 3.0.0 [beta 3] (April 13th, 2010)*
+
+* No changes
+
+
*Rails 3.0.0 [beta 1] (February 4, 2010)*
* Add support for errors in JSON format. #1956 [Fabien Jakimowicz]
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index a5a7a9b904..da370303c6 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,4 +1,6 @@
-*Rails 3.0.0 [beta 3] (pending)*
+*Rails 3.0.0 [beta 3] (April 13th, 2010)*
+
+* Improve transliteration quality. #4374 [Norman Clarke]
* Speed up and add Ruby 1.9 support for ActiveSupport::Multibyte::Chars#tidy_bytes. #4350 [Norman Clarke]
diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb
index 725acad43a..d2bcd7a778 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute.rb
@@ -1,5 +1,4 @@
require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/module/remove_method'
class Class
@@ -41,21 +40,31 @@ class Class
def class_attribute(*attrs)
instance_writer = !attrs.last.is_a?(Hash) || attrs.pop[:instance_writer]
- s = singleton_class
- attrs.each do |attr|
- s.send(:define_method, attr) { }
- s.send(:define_method, :"#{attr}?") { !!send(attr) }
- s.send(:define_method, :"#{attr}=") do |value|
- singleton_class.remove_possible_method(attr)
- singleton_class.send(:define_method, attr) { value }
- end
+ attrs.each do |name|
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def self.#{name}() nil end
+ def self.#{name}?() !!#{name} end
+
+ def self.#{name}=(val)
+ singleton_class.class_eval do
+ remove_possible_method(:#{name})
+ define_method(:#{name}) { val }
+ end
+ end
+
+ def #{name}
+ defined?(@#{name}) ? @#{name} : singleton_class.#{name}
+ end
- define_method(attr) { self.singleton_class.send(attr) }
- define_method(:"#{attr}?") { !!send(attr) }
- define_method(:"#{attr}=") do |value|
- singleton_class.remove_possible_method(attr)
- singleton_class.send(:define_method, attr) { value }
- end if instance_writer
+ def #{name}?
+ !!#{name}
+ end
+ RUBY
+
+ if instance_writer
+ body = "def #{name}=(value) @#{name} = value end"
+ class_eval body, __FILE__, __LINE__ - 1
+ end
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/proc.rb b/activesupport/lib/active_support/core_ext/proc.rb
index 71b413a88a..94bb5fb0cb 100644
--- a/activesupport/lib/active_support/core_ext/proc.rb
+++ b/activesupport/lib/active_support/core_ext/proc.rb
@@ -1,4 +1,4 @@
-require "active_support/core_ext/object"
+require "active_support/core_ext/kernel/singleton_class"
class Proc #:nodoc:
def bind(object)
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index c7df62e915..b3dc5b2f3a 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -36,11 +36,13 @@ module ActiveSupport
# "ActiveRecord".underscore # => "active_record"
# "ActiveRecord::Errors".underscore # => active_record/errors
def underscore(camel_cased_word)
- camel_cased_word.to_s.gsub(/::/, '/').
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
- tr("-", "_").
- downcase
+ word = camel_cased_word.to_s.dup
+ word.gsub!(/::/, '/')
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
+ word.tr!("-", "_")
+ word.downcase!
+ word
end
# Replaces underscores with dashes in the string.
diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb
index ca591abc7d..9c99dcfb01 100644
--- a/activesupport/lib/active_support/inflector/transliterate.rb
+++ b/activesupport/lib/active_support/inflector/transliterate.rb
@@ -1,32 +1,47 @@
# encoding: utf-8
-require 'iconv'
-require 'kconv'
require 'active_support/core_ext/string/multibyte'
module ActiveSupport
module Inflector
extend self
-
- # Replaces accented characters with their ascii equivalents.
- def transliterate(string)
- Iconv.iconv('ascii//ignore//translit', 'utf-8', string).to_s
- end
- if RUBY_VERSION >= '1.9'
- undef_method :transliterate
- def transliterate(string)
- proxy = ActiveSupport::Multibyte.proxy_class.new(string)
- proxy.normalize(:kd).gsub(/[^\x00-\x7F]+/, '')
- end
+ # UTF-8 byte => ASCII approximate UTF-8 byte(s)
+ ASCII_APPROXIMATIONS = {
+ 198 => [65, 69], # Æ => AE
+ 208 => 68, # Ð => D
+ 216 => 79, # Ø => O
+ 222 => [84, 104], # Þ => Þ
+ 223 => [115, 115], # ß => ss
+ 230 => [97, 101], # æ => ae
+ 240 => 100, # ð => d
+ 248 => 111, # ø => o
+ 254 => [116, 104], # þ => th
+ 272 => 68, # Đ => D
+ 273 => 100, # đ => đ
+ 294 => 72, # Ħ => H
+ 295 => 104, # ħ => h
+ 305 => 105, # ı => i
+ 306 => [73, 74], # IJ =>IJ
+ 307 => [105, 106], # ij => ij
+ 312 => 107, # ĸ => k
+ 319 => 76, # Ŀ => L
+ 320 => 108, # ŀ => l
+ 321 => 76, # Ł => L
+ 322 => 108, # ł => l
+ 329 => 110, # ʼn => n
+ 330 => [78, 71], # Ŋ => NG
+ 331 => [110, 103], # ŋ => ng
+ 338 => [79, 69], # Π=> OE
+ 339 => [111, 101], # œ => oe
+ 358 => 84, # Ŧ => T
+ 359 => 116 # ŧ => t
+ }
- # The iconv transliteration code doesn't function correctly
- # on some platforms, but it's very fast where it does function.
- elsif "foo" != (Inflector.transliterate("föö") rescue nil)
- undef_method :transliterate
- def transliterate(string)
- string.mb_chars.normalize(:kd). # Decompose accented characters
- gsub(/[^\x00-\x7F]+/, '') # Remove anything non-ASCII entirely (e.g. diacritics).
- end
+ # Replaces accented characters with an ASCII approximation, or deletes it if none exsits.
+ def transliterate(string)
+ ActiveSupport::Multibyte::Chars.new(string).tidy_bytes.normalize(:d).unpack("U*").map do |char|
+ ASCII_APPROXIMATIONS[char] || (char if char < 128)
+ end.compact.flatten.pack("U*")
end
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
@@ -45,8 +60,6 @@ module ActiveSupport
# <%= link_to(@person.name, person_path(@person)) %>
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
def parameterize(string, sep = '-')
- # remove malformed utf8 characters
- string = string.toutf8 unless string.is_utf8?
# replace accented chars with their ascii equivalents
parameterized_string = transliterate(string)
# Turn unwanted chars into the separator
@@ -59,6 +72,6 @@ module ActiveSupport
parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
end
parameterized_string.downcase
- end
+ end
end
end
diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb
index 29f87ac302..59515dad32 100644
--- a/activesupport/test/inflector_test_cases.rb
+++ b/activesupport/test/inflector_test_cases.rb
@@ -188,7 +188,10 @@ module InflectorTestCases
StringToParameterizedAndNormalized = {
"Malmö" => "malmo",
"Garçons" => "garcons",
- "Ops\331" => "ops"
+ "Ops\331" => "opsu",
+ "Ærøskøbing" => "aeroskobing",
+ "Aßlar" => "asslar",
+ "Japanese: 日本語" => "japanese"
}
UnderscoreToHuman = {
diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb
new file mode 100644
index 0000000000..d689b6be73
--- /dev/null
+++ b/activesupport/test/transliterate_test.rb
@@ -0,0 +1,50 @@
+# encoding: utf-8
+require 'abstract_unit'
+require 'active_support/inflector/transliterate'
+
+class TransliterateTest < Test::Unit::TestCase
+
+ APPROXIMATIONS = {
+ "À"=>"A", "Á"=>"A", "Â"=>"A", "Ã"=>"A", "Ä"=>"A", "Å"=>"A", "Æ"=>"AE",
+ "Ç"=>"C", "È"=>"E", "É"=>"E", "Ê"=>"E", "Ë"=>"E", "Ì"=>"I", "Í"=>"I",
+ "Î"=>"I", "Ï"=>"I", "Ð"=>"D", "Ñ"=>"N", "Ò"=>"O", "Ó"=>"O", "Ô"=>"O",
+ "Õ"=>"O", "Ö"=>"O", "Ø"=>"O", "Ù"=>"U", "Ú"=>"U", "Û"=>"U", "Ü"=>"U",
+ "Ý"=>"Y", "Þ"=>"Th", "ß"=>"ss", "à"=>"a", "á"=>"a", "â"=>"a", "ã"=>"a",
+ "ä"=>"a", "å"=>"a", "æ"=>"ae", "ç"=>"c", "è"=>"e", "é"=>"e", "ê"=>"e",
+ "ë"=>"e", "ì"=>"i", "í"=>"i", "î"=>"i", "ï"=>"i", "ð"=>"d", "ñ"=>"n",
+ "ò"=>"o", "ó"=>"o", "ô"=>"o", "õ"=>"o", "ö"=>"o", "ø"=>"o", "ù"=>"u",
+ "ú"=>"u", "û"=>"u", "ü"=>"u", "ý"=>"y", "þ"=>"th", "ÿ"=>"y", "Ā"=>"A",
+ "ā"=>"a", "Ă"=>"A", "ă"=>"a", "Ą"=>"A", "ą"=>"a", "Ć"=>"C", "ć"=>"c",
+ "Ĉ"=>"C", "ĉ"=>"c", "Ċ"=>"C", "ċ"=>"c", "Č"=>"C", "č"=>"c", "Ď"=>"D",
+ "ď"=>"d", "Đ"=>"D", "đ"=>"d", "Ē"=>"E", "ē"=>"e", "Ĕ"=>"E", "ĕ"=>"e",
+ "Ė"=>"E", "ė"=>"e", "Ę"=>"E", "ę"=>"e", "Ě"=>"E", "ě"=>"e", "Ĝ"=>"G",
+ "ĝ"=>"g", "Ğ"=>"G", "ğ"=>"g", "Ġ"=>"G", "ġ"=>"g", "Ģ"=>"G", "ģ"=>"g",
+ "Ĥ"=>"H", "ĥ"=>"h", "Ħ"=>"H", "ħ"=>"h", "Ĩ"=>"I", "ĩ"=>"i", "Ī"=>"I",
+ "ī"=>"i", "Ĭ"=>"I", "ĭ"=>"i", "Į"=>"I", "į"=>"i", "İ"=>"I", "ı"=>"i",
+ "IJ"=>"IJ", "ij"=>"ij", "Ĵ"=>"J", "ĵ"=>"j", "Ķ"=>"K", "ķ"=>"k", "ĸ"=>"k",
+ "Ĺ"=>"L", "ĺ"=>"l", "Ļ"=>"L", "ļ"=>"l", "Ľ"=>"L", "ľ"=>"l", "Ŀ"=>"L",
+ "ŀ"=>"l", "Ł"=>"L", "ł"=>"l", "Ń"=>"N", "ń"=>"n", "Ņ"=>"N", "ņ"=>"n",
+ "Ň"=>"N", "ň"=>"n", "ʼn"=>"n", "Ŋ"=>"NG", "ŋ"=>"ng", "Ō"=>"O", "ō"=>"o",
+ "Ŏ"=>"O", "ŏ"=>"o", "Ő"=>"O", "ő"=>"o", "Œ"=>"OE", "œ"=>"oe", "Ŕ"=>"R",
+ "ŕ"=>"r", "Ŗ"=>"R", "ŗ"=>"r", "Ř"=>"R", "ř"=>"r", "Ś"=>"S", "ś"=>"s",
+ "Ŝ"=>"S", "ŝ"=>"s", "Ş"=>"S", "ş"=>"s", "Š"=>"S", "š"=>"s", "Ţ"=>"T",
+ "ţ"=>"t", "Ť"=>"T", "ť"=>"t", "Ŧ"=>"T", "ŧ"=>"t", "Ũ"=>"U", "ũ"=>"u",
+ "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u", "Ů"=>"U", "ů"=>"u", "Ű"=>"U",
+ "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W", "ŵ"=>"w", "Ŷ"=>"Y", "ŷ"=>"y",
+ "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z", "ż"=>"z", "Ž"=>"Z", "ž"=>"z"
+ }
+
+ def test_transliterate_should_not_change_ascii_chars
+ (0..127).each do |byte|
+ char = [byte].pack("U")
+ assert_equal char, ActiveSupport::Inflector.transliterate(char)
+ end
+ end
+
+ def test_should_convert_accented_chars_to_approximate_ascii_chars
+ APPROXIMATIONS.each do |given, expected|
+ assert_equal expected, ActiveSupport::Inflector.transliterate(given)
+ end
+ end
+
+end
diff --git a/bin/rails b/bin/rails
new file mode 100755
index 0000000000..4a74726e9b
--- /dev/null
+++ b/bin/rails
@@ -0,0 +1,7 @@
+begin
+ require "rails/cli"
+rescue LoadError
+ railties_path = File.expand_path('../../railties/lib', __FILE__)
+ $:.unshift(railties_path)
+ require "rails/cli"
+end
diff --git a/ci/site_config.rb b/ci/site_config.rb
index 47b7e92120..ac7f658237 100644
--- a/ci/site_config.rb
+++ b/ci/site_config.rb
@@ -69,4 +69,4 @@ BuildReaper.number_of_builds_to_keep = 100
# any files that you'd like to override in cruise, keep in ~/.cruise, and copy over when this file is loaded like this
site_css = CRUISE_DATA_ROOT + "/site.css"
-FileUtils.cp site_css, RAILS_ROOT + "/public/stylesheets/site.css" if File.exists? site_css
+FileUtils.cp site_css, Rails.root + "/public/stylesheets/site.css" if File.exists? site_css
diff --git a/rails.gemspec b/rails.gemspec
index 9c3204242f..3b1dfe9456 100644
--- a/rails.gemspec
+++ b/rails.gemspec
@@ -14,9 +14,10 @@ Gem::Specification.new do |s|
s.email = 'david@loudthinking.com'
s.homepage = 'http://www.rubyonrails.org'
s.rubyforge_project = 'rails'
-
- s.files = []
- s.require_path = []
+
+ s.bindir = 'bin'
+ s.executables = ['rails']
+ s.default_executable = 'rails'
s.add_dependency('activesupport', version)
s.add_dependency('actionpack', version)
@@ -24,5 +25,5 @@ Gem::Specification.new do |s|
s.add_dependency('activeresource', version)
s.add_dependency('actionmailer', version)
s.add_dependency('railties', version)
- s.add_dependency('bundler', '>= 0.9.14')
+ s.add_dependency('bundler', '>= 0.9.19')
end
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index 82684e4614..a326e29d27 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,5 +1,8 @@
+*Rails 3.0.0 [beta 3] (April 13th, 2010)*
+
* Renamed config.cookie_secret to config.secret_token and pass it as env key. [JV]
+
*Rails 3.0.0 [beta 2] (April 1st, 2010)*
* Session store configuration has changed [YK & CL]
diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile
index fea0185c4c..7df4f8f719 100644
--- a/railties/guides/source/initialization.textile
+++ b/railties/guides/source/initialization.textile
@@ -2642,7 +2642,7 @@ The method +find_with_root_flag+ is defined on +Rails::Engine+ (the superclass o
root = File.exist?("#{root_path}/#{flag}") ? root_path : default
raise "Could not find root path for #{self}" unless root
- RUBY_PLATFORM =~ /(:?mswin|mingw)/ ?
+ RUBY_PLATFORM =~ /mswin|mingw/ ?
Pathname.new(root).expand_path : Pathname.new(root).realpath
end
</ruby>
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index 32b1e30502..7ac5bc8d3a 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -609,10 +609,10 @@ resources :photos, :path_names => { :new => 'make', :edit => 'change' }
This would cause the routing to recognize URLs such as
-<pre>
+<plain>
/photos/make
/photos/1/change
-</pre>
+</plain>
NOTE: The actual action names aren't changed by this option. The two URLs shown would still route to the new and edit actions.
diff --git a/railties/bin/rails b/railties/lib/rails/cli.rb
index 173f122445..173f122445 100755..100644
--- a/railties/bin/rails
+++ b/railties/lib/rails/cli.rb
diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb
index 1570b9ab0d..5634ee0f69 100644
--- a/railties/lib/rails/commands/runner.rb
+++ b/railties/lib/rails/commands/runner.rb
@@ -18,7 +18,7 @@ ARGV.clone.options do |opts|
opts.on("-h", "--help",
"Show this help message.") { $stderr.puts opts; exit }
- if RUBY_PLATFORM !~ /mswin/
+ if RUBY_PLATFORM !~ /mswin|mingw/
opts.separator ""
opts.separator "You can also use runner as a shebang line for your scripts like this:"
opts.separator "-------------------------------------------------------------"
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index c284840a38..0f33b40a13 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -119,7 +119,7 @@ module Rails
root = File.exist?("#{root_path}/#{flag}") ? root_path : default
raise "Could not find root path for #{self}" unless root
- RUBY_PLATFORM =~ /(:?mswin|mingw)/ ?
+ RUBY_PLATFORM =~ /mswin|mingw/ ?
Pathname.new(root).expand_path : Pathname.new(root).realpath
end
end
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
index 0615a34a85..9b83207b3f 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
@@ -1,13 +1,14 @@
<%%= form_for(@<%= singular_name %>) do |f| %>
<%% if @<%= singular_name %>.errors.any? %>
- <div id="errorExplanation">
- <h2><%%= pluralize(@<%= singular_name %>.errors.count, "error") %> prohibited this <%= singular_name %> from being saved:</h2>
- <ul>
- <%% @<%= singular_name %>.errors.full_messages.each do |msg| %>
- <li><%%= msg %></li>
- <%% end %>
- </ul>
- </div>
+ <div id="error_explanation">
+ <h2><%%= pluralize(@<%= singular_name %>.errors.count, "error") %> prohibited this <%= singular_name %> from being saved:</h2>
+
+ <ul>
+ <%% @<%= singular_name %>.errors.full_messages.each do |msg| %>
+ <li><%%= msg %></li>
+ <%% end %>
+ </ul>
+ </div>
<%% end %>
<% for attribute in attributes -%>
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index bb2a080286..6818fafbe9 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -265,7 +265,7 @@ module Rails::Generators
"/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
"/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
"/opt/lampp/var/mysql/mysql.sock" # xampp for linux
- ].find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/
+ ].find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /mswin|mingw/
end
def empty_directory_with_gitkeep(destination, config = {})
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index 0dd10f3f2d..f751c4519d 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -2,8 +2,10 @@ source 'http://rubygems.org'
<%- if options.dev? -%>
gem 'rails', :path => '<%= Rails::Generators::RAILS_DEV_PATH %>'
+gem 'arel', :git => 'git://github.com/rails/arel.git'
<%- elsif options.edge? -%>
gem 'rails', :git => 'git://github.com/rails/rails.git'
+gem 'arel', :git => 'git://github.com/rails/arel.git'
<%- else -%>
gem 'rails', '<%= Rails::VERSION::STRING %>'
diff --git a/railties/lib/rails/generators/rails/app/templates/config/boot.rb b/railties/lib/rails/generators/rails/app/templates/config/boot.rb
index 3971a07012..62a8ccc273 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/boot.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/boot.rb
@@ -1,14 +1,9 @@
-# Use locked gems if present.
-begin
- require File.expand_path('../../.bundle/environment', __FILE__)
+require 'rubygems'
-rescue LoadError
- # Otherwise, use RubyGems.
- require 'rubygems'
-
- # And set up the gems listed in the Gemfile.
- if File.exist?(File.expand_path('../../Gemfile', __FILE__))
- require 'bundler'
- Bundler.setup
- end
-end
+# Set up gems listed in the Gemfile.
+gemfile = File.expand_path('../../Gemfile', __FILE__)
+if File.exist?(gemfile)
+ ENV['BUNDLE_GEMFILE'] = gemfile
+ require 'bundler'
+ Bundler.setup
+end \ No newline at end of file
diff --git a/railties/lib/rails/generators/rails/model/USAGE b/railties/lib/rails/generators/rails/model/USAGE
index d97e9ac103..db98a2dd1b 100644
--- a/railties/lib/rails/generators/rails/model/USAGE
+++ b/railties/lib/rails/generators/rails/model/USAGE
@@ -15,6 +15,10 @@ Description:
Finally, if --parent option is given, it's used as superclass of the
created model. This allows you create Single Table Inheritance models.
+ If you pass a namespaced model name (e.g. admin/account or Admin::Account)
+ then the generator will create a module with a table_name_prefix method
+ to prefix the model's table name with the module name (e.g. admin_account)
+
Examples:
`rails generate model account`
@@ -28,3 +32,14 @@ Examples:
`rails generate model post title:string body:text published:boolean`
Creates a Post model with a string title, text body, and published flag.
+
+ `rails generate model admin/account`
+
+ For ActiveRecord and TestUnit it creates:
+
+ Module: app/models/admin.rb
+ Model: app/models/admin/account.rb
+ Test: test/unit/admin/account_test.rb
+ Fixtures: test/fixtures/admin_accounts.yml
+ Migration: db/migrate/XXX_add_admin_accounts.rb
+
diff --git a/railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css b/railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css
index f3f46d8b98..9f2056a702 100644
--- a/railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css
+++ b/railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css
@@ -34,7 +34,7 @@ div.field, div.actions {
display: table;
}
-#errorExplanation {
+#error_explanation {
width: 450px;
border: 2px solid red;
padding: 7px;
@@ -43,7 +43,7 @@ div.field, div.actions {
background-color: #f0f0f0;
}
-#errorExplanation h2 {
+#error_explanation h2 {
text-align: left;
font-weight: bold;
padding: 5px 5px 5px 15px;
@@ -54,7 +54,7 @@ div.field, div.actions {
color: #fff;
}
-#errorExplanation ul li {
+#error_explanation ul li {
font-size: 12px;
list-style: square;
}
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 23b8f92abd..83f25506cb 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -30,7 +30,7 @@ end
module Kernel
def silence_stderr
old_stderr = STDERR.dup
- STDERR.reopen(RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'NUL:' : '/dev/null')
+ STDERR.reopen(RUBY_PLATFORM =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
STDERR.sync = true
yield
ensure
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index aea07efe96..b9278c0399 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -15,9 +15,6 @@ Gem::Specification.new do |s|
s.files = Dir['CHANGELOG', 'README', 'bin/**/*', 'guides/**/*', 'lib/**/{*,.[a-z]*}']
s.require_path = 'lib'
- s.bindir = 'bin'
- s.executables = ['rails']
- s.default_executable = 'rails'
s.rdoc_options << '--exclude' << '.'
s.has_rdoc = false
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 8bf0f09d6b..0f3bc1a46a 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -285,5 +285,41 @@ module ApplicationTests
get "/"
assert last_response.body =~ /csrf\-param/
end
+
+ test "config.action_controller.perform_caching = true" do
+ make_basic_app do |app|
+ app.config.action_controller.perform_caching = true
+ end
+
+ class ::OmgController < ActionController::Base
+ caches_action :index
+ def index
+ render :text => rand(1000)
+ end
+ end
+
+ get "/"
+ res = last_response.body
+ get "/"
+ assert_equal res, last_response.body # value should be unchanged
+ end
+
+ test "config.action_controller.perform_caching = false" do
+ make_basic_app do |app|
+ app.config.action_controller.perform_caching = false
+ end
+
+ class ::OmgController < ActionController::Base
+ caches_action :index
+ def index
+ render :text => rand(1000)
+ end
+ end
+
+ get "/"
+ res = last_response.body
+ get "/"
+ assert_not_equal res, last_response.body
+ end
end
end
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index f5cfd8eeca..ef415a4fed 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -27,6 +27,14 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_file "app/models/account.rb", /class Account < Admin::Account/
end
+ def test_model_with_namespace
+ run_generator ["admin/account"]
+ assert_file "app/models/admin.rb", /module Admin/
+ assert_file "app/models/admin.rb", /def self\.table_name_prefix/
+ assert_file "app/models/admin.rb", /'admin_'/
+ assert_file "app/models/admin/account.rb", /class Admin::Account < ActiveRecord::Base/
+ end
+
def test_migration
run_generator
assert_migration "db/migrate/create_accounts.rb", /class CreateAccounts < ActiveRecord::Migration/
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index e6896a1629..f0c64b92ba 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -213,7 +213,7 @@ Module.new do
require_environment = "-r #{environment}"
end
- `#{Gem.ruby} #{require_environment} #{RAILS_FRAMEWORK_ROOT}/railties/bin/rails #{tmp_path('app_template')}`
+ `#{Gem.ruby} #{require_environment} #{RAILS_FRAMEWORK_ROOT}/bin/rails #{tmp_path('app_template')}`
File.open("#{tmp_path}/app_template/config/boot.rb", 'w') do |f|
if require_environment
f.puts "Dir.chdir('#{File.dirname(environment)}') do"
diff --git a/railties/test/railties/shared_tests.rb b/railties/test/railties/shared_tests.rb
index 83d25be5db..20328d402d 100644
--- a/railties/test/railties/shared_tests.rb
+++ b/railties/test/railties/shared_tests.rb
@@ -264,7 +264,7 @@ YAML
Rails.application.routes.draw do
namespace :admin do
namespace :foo do
- match "bar", :to => "admin/foo/bar#index"
+ match "bar", :to => "bar#index"
end
end
end
diff --git a/release.rb b/release.rb
new file mode 100644
index 0000000000..2076515f0e
--- /dev/null
+++ b/release.rb
@@ -0,0 +1,11 @@
+version = ARGV.pop
+
+%w( activesupport activemodel activerecord activeresource actionpack actionmailer railties ).each do |framework|
+ puts "Building and pushing #{framework}..."
+ `cd #{framework} && gem build #{framework}.gemspec && gem push #{framework}-#{version}.gem && rm #{framework}-#{version}.gem`
+end
+
+puts "Building and pushing Rails..."
+`gem build rails.gemspec`
+`gem push rails-#{version}.gem`
+`rm rails-#{version}.gem` \ No newline at end of file