aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rw-r--r--Gemfile9
-rw-r--r--actionmailer/CHANGELOG.md3
-rw-r--r--actionmailer/actionmailer.gemspec2
-rw-r--r--actionmailer/lib/action_mailer.rb1
-rw-r--r--actionmailer/lib/action_mailer/base.rb14
-rw-r--r--actionmailer/test/base_test.rb42
-rw-r--r--actionpack/CHANGELOG.md40
-rw-r--r--actionpack/lib/action_controller/test_case.rb3
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb34
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb10
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb11
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb1
-rw-r--r--actionpack/lib/action_view/renderer/renderer.rb14
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb2
-rw-r--r--actionpack/test/controller/flash_hash_test.rb21
-rw-r--r--actionpack/test/controller/render_test.rb10
-rw-r--r--actionpack/test/dispatch/request_test.rb15
-rw-r--r--actionpack/test/dispatch/routing_test.rb20
-rw-r--r--activemodel/examples/validations.rb6
-rw-r--r--activemodel/lib/active_model/deprecated_mass_assignment_security.rb10
-rw-r--r--activemodel/lib/active_model/secure_password.rb5
-rw-r--r--activemodel/test/cases/secure_password_test.rb12
-rw-r--r--activerecord/CHANGELOG.md54
-rw-r--r--activerecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/associations/association.rb3
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb9
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb32
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb3
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb30
-rw-r--r--activerecord/lib/active_record/core.rb7
-rw-r--r--activerecord/lib/active_record/fixtures.rb6
-rw-r--r--activerecord/lib/active_record/migration/join_table.rb2
-rw-r--r--activerecord/lib/active_record/persistence.rb39
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb23
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb29
-rw-r--r--activerecord/test/cases/adapters/postgresql/sql_types_test.rb18
-rw-r--r--activerecord/test/cases/adapters/postgresql/timestamp_test.rb9
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb21
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb33
-rw-r--r--activerecord/test/cases/calculations_test.rb22
-rw-r--r--activerecord/test/cases/dirty_test.rb15
-rw-r--r--activerecord/test/cases/helper.rb15
-rw-r--r--activerecord/test/cases/migration/change_table_test.rb7
-rw-r--r--activerecord/test/cases/migration/create_join_table_test.rb6
-rw-r--r--activerecord/test/cases/migration/helper.rb6
-rw-r--r--activerecord/test/cases/persistence_test.rb13
-rw-r--r--activerecord/test/cases/relations_test.rb20
-rw-r--r--activerecord/test/models/reference.rb5
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb5
-rw-r--r--activesupport/CHANGELOG.md4
-rw-r--r--activesupport/lib/active_support/core_ext/hash/diff.rb2
-rw-r--r--activesupport/lib/active_support/test_case.rb2
-rw-r--r--activesupport/lib/active_support/testing/pending.rb14
-rw-r--r--activesupport/test/test_case_test.rb6
-rw-r--r--guides/source/action_mailer_basics.md18
-rw-r--r--guides/source/active_record_validations_callbacks.md8
-rw-r--r--guides/source/upgrading_ruby_on_rails.md2
-rw-r--r--railties/CHANGELOG.md10
-rw-r--r--railties/lib/rails/application/configuration.rb4
-rw-r--r--railties/lib/rails/commands/dbconsole.rb20
-rw-r--r--railties/lib/rails/commands/runner.rb2
-rw-r--r--railties/lib/rails/engine.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml57
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb2
-rw-r--r--railties/test/commands/dbconsole_test.rb8
74 files changed, 644 insertions, 288 deletions
diff --git a/.travis.yml b/.travis.yml
index c6f868498d..7e3d728872 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@ before_install:
- gem install bundler
rvm:
- 1.9.3
+ - 2.0.0
env:
- "GEM=railties"
- "GEM=ap,am,amo,as"
@@ -23,3 +24,6 @@ notifications:
rooms:
- secure: "CGWvthGkBKNnTnk9YSmf9AXKoiRI33fCl5D3jU4nx3cOPu6kv2R9nMjt9EAo\nOuS4Q85qNSf4VNQ2cUPNiNYSWQ+XiTfivKvDUw/QW9r1FejYyeWarMsSBWA+\n0fADjF1M2dkDIVLgYPfwoXEv7l+j654F1KLKB69F0F/netwP9CQ="
bundler_args: --path vendor/bundle
+matrix:
+ allow_failures:
+ - rvm: 2.0.0
diff --git a/Gemfile b/Gemfile
index cb8f85b0a3..35bcb68fb2 100644
--- a/Gemfile
+++ b/Gemfile
@@ -23,10 +23,6 @@ gem 'uglifier', require: false
gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: 'master'
group :doc do
- # The current sdoc cannot generate GitHub links due
- # to a bug, but the PR that fixes it has been there
- # for some weeks unapplied. As a temporary solution
- # this is our own fork with the fix.
gem 'sdoc', github: 'voloko/sdoc'
gem 'redcarpet', '~> 2.2.2', platforms: :ruby
gem 'w3c_validators'
@@ -42,8 +38,7 @@ instance_eval File.read local_gemfile if File.exists? local_gemfile
platforms :mri do
group :test do
gem 'ruby-prof', '~> 0.11.2' if RUBY_VERSION < '2.0'
- gem 'debugger' if !ENV['TRAVIS'] && RUBY_VERSION < '2.0' && RUBY_PATCHLEVEL < 327
-
+ gem 'debugger' if !ENV['TRAVIS'] && RUBY_VERSION < '2.0'
end
end
@@ -56,7 +51,7 @@ platforms :ruby do
group :db do
gem 'pg', '>= 0.11.0'
- gem 'mysql', '>= 2.8.1' if RUBY_VERSION < '2.0.0'
+ gem 'mysql', '>= 2.9.0'
gem 'mysql2', '>= 0.3.10'
end
end
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 28a5c0ab71..af91907f46 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,5 +1,8 @@
## Rails 4.0.0 (unreleased) ##
+* Explicit multipart messages no longer set the order of the MIME parts.
+ *Nate Berkopec*
+
* Do not render views when mail() isn't called.
Fix #7761
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index 0177a13e50..e9f979f34b 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -21,5 +21,5 @@ Gem::Specification.new do |s|
s.add_dependency 'actionpack', version
- s.add_dependency 'mail', '~> 2.4.4'
+ s.add_dependency 'mail', '~> 2.5.2'
end
diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb
index cfbe2f1cbd..a9642dc695 100644
--- a/actionmailer/lib/action_mailer.rb
+++ b/actionmailer/lib/action_mailer.rb
@@ -44,4 +44,5 @@ module ActionMailer
autoload :MailHelper
autoload :TestCase
autoload :TestHelper
+ autoload :QueuedMessage
end
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 6a9828fde7..2b533ad054 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -698,7 +698,7 @@ module ActionMailer
assignable.each { |k, v| m[k] = v }
# Render the templates and blocks
- responses, explicit_order = collect_responses_and_parts_order(headers, &block)
+ responses = collect_responses(headers, &block)
create_parts_from_responses(m, responses)
# Setup content type, reapply charset and handle parts order
@@ -706,8 +706,7 @@ module ActionMailer
m.charset = charset
if m.multipart?
- parts_order ||= explicit_order || headers[:parts_order]
- m.body.set_sort_order(parts_order)
+ m.body.set_sort_order(headers[:parts_order])
m.body.sort_parts!
end
@@ -742,14 +741,13 @@ module ActionMailer
I18n.t(:subject, scope: [mailer_scope, action_name], default: action_name.humanize)
end
- def collect_responses_and_parts_order(headers) #:nodoc:
- responses, parts_order = [], nil
+ def collect_responses(headers) #:nodoc:
+ responses = []
if block_given?
collector = ActionMailer::Collector.new(lookup_context) { render(action_name) }
yield(collector)
- parts_order = collector.responses.map { |r| r[:content_type] }
- responses = collector.responses
+ responses = collector.responses
elsif headers[:body]
responses << {
body: headers.delete(:body),
@@ -769,7 +767,7 @@ module ActionMailer
end
end
- [responses, parts_order]
+ responses
end
def each_template(paths, name, &block) #:nodoc:
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index b07b352082..3c21886502 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -322,19 +322,6 @@ class BaseTest < ActiveSupport::TestCase
assert_not_nil(mail.content_type_parameters[:boundary])
end
- test "explicit multipart does not sort order" do
- order = ["text/html", "text/plain"]
- with_default BaseMailer, parts_order: order do
- email = BaseMailer.explicit_multipart
- assert_equal("text/plain", email.parts[0].mime_type)
- assert_equal("text/html", email.parts[1].mime_type)
-
- email = BaseMailer.explicit_multipart(parts_order: order.reverse)
- assert_equal("text/plain", email.parts[0].mime_type)
- assert_equal("text/html", email.parts[1].mime_type)
- end
- end
-
test "explicit multipart with attachments creates nested parts" do
email = BaseMailer.explicit_multipart(attachments: true)
assert_equal("application/pdf", email.parts[0].mime_type)
@@ -349,10 +336,10 @@ class BaseTest < ActiveSupport::TestCase
email = BaseMailer.explicit_multipart_templates
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
- assert_equal("text/html", email.parts[0].mime_type)
- assert_equal("HTML Explicit Multipart Templates", email.parts[0].body.encoded)
- assert_equal("text/plain", email.parts[1].mime_type)
- assert_equal("TEXT Explicit Multipart Templates", email.parts[1].body.encoded)
+ assert_equal("text/plain", email.parts[0].mime_type)
+ assert_equal("TEXT Explicit Multipart Templates", email.parts[0].body.encoded)
+ assert_equal("text/html", email.parts[1].mime_type)
+ assert_equal("HTML Explicit Multipart Templates", email.parts[1].body.encoded)
end
test "explicit multipart with format.any" do
@@ -387,10 +374,23 @@ class BaseTest < ActiveSupport::TestCase
email = BaseMailer.explicit_multipart_with_one_template
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
- assert_equal("text/html", email.parts[0].mime_type)
- assert_equal("[:html]", email.parts[0].body.encoded)
- assert_equal("text/plain", email.parts[1].mime_type)
- assert_equal("[:text]", email.parts[1].body.encoded)
+ assert_equal("text/plain", email.parts[0].mime_type)
+ assert_equal("[:text]", email.parts[0].body.encoded)
+ assert_equal("text/html", email.parts[1].mime_type)
+ assert_equal("[:html]", email.parts[1].body.encoded)
+ end
+
+ test "explicit multipart with sort order" do
+ order = ["text/html", "text/plain"]
+ with_default BaseMailer, parts_order: order do
+ email = BaseMailer.explicit_multipart
+ assert_equal("text/html", email.parts[0].mime_type)
+ assert_equal("text/plain", email.parts[1].mime_type)
+
+ email = BaseMailer.explicit_multipart(parts_order: order.reverse)
+ assert_equal("text/plain", email.parts[0].mime_type)
+ assert_equal("text/html", email.parts[1].mime_type)
+ end
end
# Class level API with method missing
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 1ebc75ed2f..85a83ed7d9 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,45 @@
## Rails 4.0.0 (unreleased) ##
+* Allow setting a symbol as path in scope on routes. This is now allowed:
+
+ scope :api do
+ resources :users
+ end
+
+ It is also possible to pass multiple symbols to scope to shorten multiple nested scopes:
+
+ scope :api do
+ scope :v1 do
+ resources :users
+ end
+ end
+
+ can be rewritten as:
+
+ scope :api, :v1 do
+ resources :users
+ end
+
+ *Guillermo Iguaran*
+
+* Fix error when using a non-hash query argument named "params" in `url_for`.
+
+ Before:
+
+ url_for(params: "") # => undefined method `reject!' for "":String
+
+ After:
+
+ url_for(params: "") # => http://www.example.com?params=
+
+ *tumayun + Carlos Antonio da Silva*
+
+* Render every partial with a new `ActionView::PartialRenderer`. This resolves
+ issues when rendering nested partials.
+ Fix #8197
+
+ *Yves Senn*
+
* Introduce `ActionView::Template::Handlers::ERB.escape_whitelist`. This is a list
of mime types where template text is not html escaped by default. It prevents `Jack & Joe`
from rendering as `Jack &amp; Joe` for the whitelisted mime types. The default whitelist
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 5aecb59df9..be8055955d 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -509,7 +509,7 @@ module ActionController
@request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
@request.session.update(session) if session
- @request.session["flash"] = @request.flash.update(flash || {})
+ @request.flash.update(flash || {})
@controller.request = @request
@controller.response = @response
@@ -526,6 +526,7 @@ module ActionController
@response.prepare!
@assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
+ @request.session['flash'] = @request.flash.to_session_value
@request.session.delete('flash') if @request.session['flash'].blank?
@response
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 9a7e8a5a9c..bced7d84c0 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -28,7 +28,7 @@ module ActionDispatch
path = options.delete(:script_name).to_s.chomp("/")
path << options.delete(:path).to_s
- params = options[:params] || {}
+ params = options[:params].is_a?(Hash) ? options[:params] : options.slice(:params)
params.reject! { |_,v| v.to_param.nil? }
result = build_host_url(options)
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 9928b7cc3a..7b18c57420 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -4,7 +4,7 @@ module ActionDispatch
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
def flash
- @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new).tap(&:sweep)
+ @env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
end
end
@@ -70,16 +70,30 @@ module ActionDispatch
end
end
- # Implementation detail: please do not change the signature of the
- # FlashHash class. Doing that will likely affect all Rails apps in
- # production as the FlashHash currently stored in their sessions will
- # become invalid.
class FlashHash
include Enumerable
- def initialize #:nodoc:
- @discard = Set.new
- @flashes = {}
+ def self.from_session_value(value)
+ flash = case value
+ when FlashHash # Rails 3.1, 3.2
+ new(value.instance_variable_get(:@flashes), value.instance_variable_get(:@used))
+ when Hash # Rails 4.0
+ new(value['flashes'], value['discard'])
+ else
+ new
+ end
+
+ flash.tap(&:sweep)
+ end
+
+ def to_session_value
+ return nil if empty?
+ {'discard' => @discard.to_a, 'flashes' => @flashes}
+ end
+
+ def initialize(flashes = {}, discard = []) #:nodoc:
+ @discard = Set.new(discard)
+ @flashes = flashes
@now = nil
end
@@ -223,7 +237,7 @@ module ActionDispatch
if flash_hash
if !flash_hash.empty? || session.key?('flash')
- session["flash"] = flash_hash
+ session["flash"] = flash_hash.to_session_value
new_hash = flash_hash.dup
else
new_hash = flash_hash
@@ -233,7 +247,7 @@ module ActionDispatch
end
if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
- session.key?('flash') && session['flash'].empty?
+ session.key?('flash') && session['flash'].nil?
session.delete('flash')
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index d7f83a1cc6..ce5f89ee5b 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -95,9 +95,17 @@ module ActionDispatch
end
# This cookie store helps you upgrading apps that use +CookieStore+ to the new default +EncryptedCookieStore+
+ # To use this CookieStore set
+ #
+ # Myapp::Application.config.session_store :upgrade_signature_to_encryption_cookie_store, key: '_myapp_session'
#
- # To use this CookieStore set MyApp.config.session_store :upgrade_signature_to_encryption_cookie_store, key: '_myapp_session'
# in your config/initializers/session_store.rb
+ #
+ # You will also need to add
+ #
+ # Myapp::Application.config.secret_key_base = 'some secret'
+ #
+ # in your config/initializers/secret_token.rb, but do not remove +Myapp::Application.config.secret_token = 'some secret'+
class UpgradeSignatureToEncryptionCookieStore < EncryptedCookieStore
private
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index d6fe436b68..05cbcf709e 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -644,7 +644,7 @@ module ActionDispatch
options = args.extract_options!
options = options.dup
- options[:path] = args.first if args.first.is_a?(String)
+ options[:path] = args.flatten.join('/') if args.any?
recover = {}
options[:constraints] ||= {}
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index 8f17ee05be..79dff7d121 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -36,16 +36,19 @@ module ActionDispatch
#
# # Test a custom route
# assert_recognizes({controller: 'items', action: 'show', id: '1'}, 'view/item1')
- def assert_recognizes(expected_options, path, extras={}, message=nil)
+ def assert_recognizes(expected_options, path, extras={}, msg=nil)
request = recognized_request_for(path, extras)
expected_options = expected_options.clone
expected_options.stringify_keys!
- message ||= sprintf("The recognized options <%s> did not match <%s>, difference: <%s>",
- request.path_parameters, expected_options, diff(expected_options, request.path_parameters))
- assert_equal(expected_options, request.path_parameters, message)
+ msg = message(msg, "") {
+ sprintf("The recognized options <%s> did not match <%s>, difference:",
+ request.path_parameters, expected_options)
+ }
+
+ assert_equal(expected_options, request.path_parameters, msg)
end
# Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 36d557e1a3..f5fdf766ad 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -1,4 +1,3 @@
-
module ActionView
# = Action View Partials
#
diff --git a/actionpack/lib/action_view/renderer/renderer.rb b/actionpack/lib/action_view/renderer/renderer.rb
index bf1b5a7d22..30a0c4be70 100644
--- a/actionpack/lib/action_view/renderer/renderer.rb
+++ b/actionpack/lib/action_view/renderer/renderer.rb
@@ -33,22 +33,12 @@ module ActionView
# Direct accessor to template rendering.
def render_template(context, options) #:nodoc:
- _template_renderer.render(context, options)
+ TemplateRenderer.new(@lookup_context).render(context, options)
end
# Direct access to partial rendering.
def render_partial(context, options, &block) #:nodoc:
- _partial_renderer.render(context, options, block)
- end
-
- private
-
- def _template_renderer #:nodoc:
- @_template_renderer ||= TemplateRenderer.new(@lookup_context)
- end
-
- def _partial_renderer #:nodoc:
- @_partial_renderer ||= PartialRenderer.new(@lookup_context)
+ PartialRenderer.new(@lookup_context).render(context, options, block)
end
end
end
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index 2a5ea5a711..4d5c5db80c 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -41,7 +41,7 @@ module ActionView
# Renders the given template. A string representing the layout can be
# supplied as well.
- def render_template(template, layout_name = nil, locals = {}) #:nodoc:
+ def render_template(template, layout_name = nil, locals = nil) #:nodoc:
view, locals = @view, locals || {}
render_with_layout(layout_name, locals) do |layout|
diff --git a/actionpack/test/controller/flash_hash_test.rb b/actionpack/test/controller/flash_hash_test.rb
index ccca0dac17..5490d9394b 100644
--- a/actionpack/test/controller/flash_hash_test.rb
+++ b/actionpack/test/controller/flash_hash_test.rb
@@ -46,6 +46,27 @@ module ActionDispatch
assert_equal({'foo' => 'bar'}, @hash.to_hash)
end
+ def test_to_session_value
+ @hash['foo'] = 'bar'
+ assert_equal({'flashes' => {'foo' => 'bar'}, 'discard' => []}, @hash.to_session_value)
+
+ @hash.discard('foo')
+ assert_equal({'flashes' => {'foo' => 'bar'}, 'discard' => %w[foo]}, @hash.to_session_value)
+
+ @hash.now['qux'] = 1
+ assert_equal({'flashes' => {'foo' => 'bar', 'qux' => 1}, 'discard' => %w[foo qux]}, @hash.to_session_value)
+
+ @hash.sweep
+ assert_equal(nil, @hash.to_session_value)
+ end
+
+ def test_from_session_value
+ rails_3_2_cookie = 'BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJWY4ZTFiODE1MmJhNzYwOWMyOGJiYjE3ZWM5MjYzYmE3BjsAVEkiCmZsYXNoBjsARm86JUFjdGlvbkRpc3BhdGNoOjpGbGFzaDo6Rmxhc2hIYXNoCToKQHVzZWRvOghTZXQGOgpAaGFzaHsAOgxAY2xvc2VkRjoNQGZsYXNoZXN7BkkiDG1lc3NhZ2UGOwBGSSIKSGVsbG8GOwBGOglAbm93MA=='
+ session = Marshal.load(Base64.decode64(rails_3_2_cookie))
+ hash = Flash::FlashHash.from_session_value(session['flash'])
+ assert_equal({'flashes' => {'message' => 'Hello'}, 'discard' => %w[message]}, hash.to_session_value)
+ end
+
def test_empty?
assert @hash.empty?
@hash['zomg'] = 'bears'
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index aa33f01d02..859ed1466b 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -646,6 +646,10 @@ class TestController < ActionController::Base
render :partial => "customer", :spacer_template => "partial_only", :collection => [ Customer.new("david"), Customer.new("mary") ]
end
+ def partial_collection_with_spacer_which_uses_render
+ render :partial => "customer", :spacer_template => "partial_with_partial", :collection => [ Customer.new("david"), Customer.new("mary") ]
+ end
+
def partial_collection_shorthand_with_locals
render :partial => [ Customer.new("david"), Customer.new("mary") ], :locals => { :greeting => "Bonjour" }
end
@@ -1445,6 +1449,12 @@ class RenderTest < ActionController::TestCase
assert_template :partial => '_customer'
end
+ def test_partial_collection_with_spacer_which_uses_render
+ get :partial_collection_with_spacer_which_uses_render
+ assert_equal "Hello: davidpartial html\npartial with partial\nHello: mary", @response.body
+ assert_template :partial => '_customer'
+ end
+
def test_partial_collection_shorthand_with_locals
get :partial_collection_shorthand_with_locals
assert_equal "Bonjour: davidBonjour: mary", @response.body
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index e2964f9071..f2bacf3e20 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -3,7 +3,7 @@ require 'abstract_unit'
class RequestTest < ActiveSupport::TestCase
def url_for(options = {})
- options.reverse_merge!(:host => 'www.example.com')
+ options = { host: 'www.example.com' }.merge!(options)
ActionDispatch::Http::URL.url_for(options)
end
@@ -25,6 +25,8 @@ class RequestTest < ActiveSupport::TestCase
assert_equal 'http://www.example.com/', url_for(:trailing_slash => true)
assert_equal 'http://dhh:supersecret@www.example.com', url_for(:user => 'dhh', :password => 'supersecret')
assert_equal 'http://www.example.com?search=books', url_for(:params => { :search => 'books' })
+ assert_equal 'http://www.example.com?params=', url_for(:params => '')
+ assert_equal 'http://www.example.com?params=1', url_for(:params => 1)
end
test "remote ip" do
@@ -355,7 +357,6 @@ class RequestTest < ActiveSupport::TestCase
assert_equal "/of/some/uri", request.path_info
end
-
test "host with default port" do
request = stub_request 'HTTP_HOST' => 'rubyonrails.org:80'
assert_equal "rubyonrails.org", request.host_with_port
@@ -577,16 +578,16 @@ class RequestTest < ActiveSupport::TestCase
test "formats with accept header" do
request = stub_request 'HTTP_ACCEPT' => 'text/html'
request.expects(:parameters).at_least_once.returns({})
- assert_equal [ Mime::HTML ], request.formats
+ assert_equal [Mime::HTML], request.formats
request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8',
'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"
request.expects(:parameters).at_least_once.returns({})
- assert_equal with_set(Mime::XML), request.formats
+ assert_equal [Mime::XML], request.formats
request = stub_request
request.expects(:parameters).at_least_once.returns({ :format => :txt })
- assert_equal with_set(Mime::TEXT), request.formats
+ assert_equal [Mime::TEXT], request.formats
request = stub_request
request.expects(:parameters).at_least_once.returns({ :format => :unknown })
@@ -811,8 +812,4 @@ protected
ActionDispatch::Http::URL.tld_length = tld_length
ActionDispatch::Request.new(env)
end
-
- def with_set(*args)
- args
- end
end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 34606512dc..0a59d3cf9e 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -370,6 +370,14 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
scope :path => 'api' do
resource :me
get '/' => 'mes#index'
+ scope :v2 do
+ resource :me, as: 'v2_me'
+ get '/' => 'mes#index'
+ end
+
+ scope :v3, :admin do
+ resource :me, as: 'v3_me'
+ end
end
get "(/:username)/followers" => "followers#index"
@@ -1467,6 +1475,18 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal 'mes#index', @response.body
end
+ def test_symbol_scope
+ get '/api/v2/me'
+ assert_equal 'mes#show', @response.body
+ assert_equal '/api/v2/me', v2_me_path
+
+ get '/api/v2'
+ assert_equal 'mes#index', @response.body
+
+ get '/api/v3/admin/me'
+ assert_equal 'mes#show', @response.body
+ end
+
def test_url_generator_for_generic_route
get 'whatever/foo/bar'
assert_equal 'foo#bar', @response.body
diff --git a/activemodel/examples/validations.rb b/activemodel/examples/validations.rb
index 0b2706076f..a56ec4db39 100644
--- a/activemodel/examples/validations.rb
+++ b/activemodel/examples/validations.rb
@@ -22,8 +22,8 @@ class Person
end
person1 = Person.new
-p person1.valid?
-person1.errors
+p person1.valid? # => false
+p person1.errors.messages # => {:name=>["can't be blank"]}
person2 = Person.new(:name => "matz")
-p person2.valid?
+p person2.valid? # => true
diff --git a/activemodel/lib/active_model/deprecated_mass_assignment_security.rb b/activemodel/lib/active_model/deprecated_mass_assignment_security.rb
index 2ea69991fc..1f409c87b9 100644
--- a/activemodel/lib/active_model/deprecated_mass_assignment_security.rb
+++ b/activemodel/lib/active_model/deprecated_mass_assignment_security.rb
@@ -5,14 +5,16 @@ module ActiveModel
module ClassMethods # :nodoc:
def attr_protected(*args)
raise "`attr_protected` is extracted out of Rails into a gem. " \
- "Please use new recommended protection model for params " \
- "or add `protected_attributes` to your Gemfile to use old one."
+ "Please use new recommended protection model for params" \
+ "(strong_parameters) or add `protected_attributes` to your " \
+ "Gemfile to use old one."
end
def attr_accessible(*args)
raise "`attr_accessible` is extracted out of Rails into a gem. " \
- "Please use new recommended protection model for params " \
- "or add `protected_attributes` to your Gemfile to use old one."
+ "Please use new recommended protection model for params" \
+ "(strong_parameters) or add `protected_attributes` to your " \
+ "Gemfile to use old one."
end
end
end
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index 081a49f749..6644b60609 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -3,6 +3,7 @@ module ActiveModel
extend ActiveSupport::Concern
class << self; attr_accessor :min_cost; end
+ self.min_cost = false
module ClassMethods
# Adds methods to set and authenticate against a BCrypt password.
@@ -13,8 +14,8 @@ module ActiveModel
# you wish to turn off validations, pass <tt>validations: false</tt> as an
# argument. You can add more validations by hand if need be.
#
- # If you don't need the confirmation validation, just don't set any
- # value to the password_confirmation attribute and the the validation
+ # If you don't need the confirmation validation, just don't set any
+ # value to the password_confirmation attribute and the the validation
# will not be triggered.
#
# You need to add bcrypt-ruby (~> 3.0.0) to Gemfile to use #has_secure_password:
diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb
index c7e93370ec..7783bb25d5 100644
--- a/activemodel/test/cases/secure_password_test.rb
+++ b/activemodel/test/cases/secure_password_test.rb
@@ -5,13 +5,18 @@ require 'models/visitor'
require 'models/administrator'
class SecurePasswordTest < ActiveModel::TestCase
-
setup do
+ ActiveModel::SecurePassword.min_cost = true
+
@user = User.new
@visitor = Visitor.new
@oauthed_user = OauthedUser.new
end
+ teardown do
+ ActiveModel::SecurePassword.min_cost = false
+ end
+
test "blank password" do
@user.password = @visitor.password = ''
assert !@user.valid?(:create), 'user should be invalid'
@@ -70,13 +75,16 @@ class SecurePasswordTest < ActiveModel::TestCase
end
end
- test "Password digest cost defaults to bcrypt default cost" do
+ test "Password digest cost defaults to bcrypt default cost when min_cost is false" do
+ ActiveModel::SecurePassword.min_cost = false
+
@user.password = "secret"
assert_equal BCrypt::Engine::DEFAULT_COST, @user.password_digest.cost
end
test "Password digest cost can be set to bcrypt min cost to speed up tests" do
ActiveModel::SecurePassword.min_cost = true
+
@user.password = "secret"
assert_equal BCrypt::Engine::MIN_COST, @user.password_digest.cost
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 580a580ba5..1a34ed441f 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,13 +1,48 @@
## Rails 4.0.0 (unreleased) ##
-* `#pluck` can be used on a relation with `select` clause
- Fix #7551
+* Prevent mass assignment to the type column of polymorphic associations when using `build`
+ Fix #8265
- Example:
+ *Yves Senn*
+
+* Deprecate calling `Relation#sum` with a block. To perform a calculation over
+ the array result of the relation, use `to_a.sum(&block)`.
+
+ *Carlos Antonio da Silva*
+
+* Fix postgresql adapter to handle BC timestamps correctly
+
+ HistoryEvent.create!(:name => "something", :occured_at => Date.new(0) - 5.years)
+
+ *Bogdan Gusiev*
+
+* When running migrations on Postgresql, the `:limit` option for `binary` and `text` columns is silently dropped.
+ Previously, these migrations caused sql exceptions, because Postgresql doesn't support limits on these types.
+
+ *Victor Costan*
+
+* Don't change STI type when calling `ActiveRecord::Base#becomes`.
+ Add `ActiveRecord::Base#becomes!` with the previous behavior.
+
+ See #3023 for more information.
+
+ *Thomas Hollstegge*
+
+* `rename_index` can be used inside a `change_table` block.
+
+ change_table :accounts do |t|
+ t.rename_index :user_id, :account_id
+ end
+
+ *Jarek Radosz*
+
+* `#pluck` can be used on a relation with `select` clause. Fix #7551
+
+ Example:
Topic.select([:approved, :id]).order(:id).pluck(:id)
- *Yves Senn*
+ *Yves Senn*
* Do not create useless database transaction when building `has_one` association.
@@ -18,7 +53,7 @@
*Bogdan Gusiev*
-* :counter_cache option for `has_many` associations to support custom named counter caches.
+* `:counter_cache` option for `has_many` associations to support custom named counter caches.
Fix #7993
*Yves Senn*
@@ -42,7 +77,7 @@
*Nikita Afanasenko*
-* Use query cache/uncache when using DATABASE_URL.
+* Use query cache/uncache when using `DATABASE_URL`.
Fix #6951.
*kennyj*
@@ -711,13 +746,6 @@
*Marc-André Lafortune*
-* Allow blocks for `count` with `ActiveRecord::Relation`, to work similar as
- `Array#count`:
-
- Person.where("age > 26").count { |person| person.gender == 'female' }
-
- *Chris Finne & Carlos Antonio da Silva*
-
* Added support to `CollectionAssociation#delete` for passing `fixnum`
or `string` values as record ids. This finds the records responding
to the `id` and executes delete on them.
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 651b17920c..d8b6d7a86b 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -234,7 +234,7 @@ module ActiveRecord
# others.size | X | X | X
# others.length | X | X | X
# others.count | X | X | X
- # others.sum(args*,&block) | X | X | X
+ # others.sum(*args) | X | X | X
# others.empty? | X | X | X
# others.clear | X | X | X
# others.delete(other,other,...) | X | X | X
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 99e7383d42..3f0e4ca999 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -232,7 +232,8 @@ module ActiveRecord
def build_record(attributes)
reflection.build_association(attributes) do |record|
- attributes = create_scope.except(*(record.changed - [reflection.foreign_key]))
+ skip_assign = [reflection.foreign_key, reflection.type].compact
+ attributes = create_scope.except(*(record.changed - skip_assign))
record.assign_attributes(attributes)
end
end
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 862ff201de..1548e68cea 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -161,15 +161,6 @@ module ActiveRecord
end
end
- # Calculate sum using SQL, not Enumerable.
- def sum(*args)
- if block_given?
- scope.sum(*args) { |*block_args| yield(*block_args) }
- else
- scope.sum(*args)
- end
- end
-
# Count all records using SQL. If the +:counter_sql+ or +:finder_sql+ option is set for the
# association, it will be used for the query. Otherwise, construct options and pass them with
# scope to the target class's +count+.
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 90701938e5..3c03cce838 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -32,21 +32,29 @@ module ActiveRecord
protected
- # We want to generate the methods via module_eval rather than define_method,
- # because define_method is slower on dispatch and uses more memory (because it
- # creates a closure).
+ # We want to generate the methods via module_eval rather than
+ # define_method, because define_method is slower on dispatch and
+ # uses more memory (because it creates a closure).
#
- # But sometimes the database might return columns with characters that are not
- # allowed in normal method names (like 'my_column(omg)'. So to work around this
- # we first define with the __temp__ identifier, and then use alias method to
- # rename it to what we want.
- def define_method_attribute(attr_name)
+ # But sometimes the database might return columns with
+ # characters that are not allowed in normal method names (like
+ # 'my_column(omg)'. So to work around this we first define with
+ # the __temp__ identifier, and then use alias method to rename
+ # it to what we want.
+ #
+ # We are also defining a constant to hold the frozen string of
+ # the attribute name. Using a constant means that we do not have
+ # to allocate an object on each call to the attribute method.
+ # Making it frozen means that it doesn't get duped when used to
+ # key the @attributes_cache in read_attribute.
+ def define_method_attribute(name)
+ safe_name = name.unpack('h*').first
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
- def __temp__
- read_attribute('#{attr_name}') { |n| missing_attribute(n, caller) }
+ def __temp__#{safe_name}
+ read_attribute(AttrNames::ATTR_#{safe_name}) { |n| missing_attribute(n, caller) }
end
- alias_method '#{attr_name}', :__temp__
- undef_method :__temp__
+ alias_method #{name.inspect}, :__temp__#{safe_name}
+ undef_method :__temp__#{safe_name}
STR
end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index 806dc5b1d2..427c61079a 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -64,8 +64,7 @@ module ActiveRecord
private
def round_usec(value)
- return unless value
- value.change(:usec => 0)
+ value.change(usec: 0) if value
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index fa9097db1f..cd33494cc3 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -9,15 +9,19 @@ module ActiveRecord
module ClassMethods
protected
- def define_method_attribute=(attr_name)
- if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP
- generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__)
- else
- generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
- write_attribute(attr_name, new_value)
- end
+
+ # See define_method_attribute in read.rb for an explanation of
+ # this code.
+ def define_method_attribute=(name)
+ safe_name = name.unpack('h*').first
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
+ def __temp__#{safe_name}=(value)
+ write_attribute(AttrNames::ATTR_#{safe_name}, value)
end
- end
+ alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
+ undef_method :__temp__#{safe_name}=
+ STR
+ end
end
# Updates the attribute identified by <tt>attr_name</tt> with the
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 38960ab873..7ec6abbc45 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -324,6 +324,7 @@ module ActiveRecord
# change_table :table do |t|
# t.column
# t.index
+ # t.rename_index
# t.timestamps
# t.change
# t.change_default
@@ -386,6 +387,13 @@ module ActiveRecord
@base.index_exists?(@table_name, column_name, options)
end
+ # Renames the given index on the table.
+ #
+ # t.rename_index(:user_id, :account_id)
+ def rename_index(index_name, new_index_name)
+ @base.rename_index(@table_name, index_name, new_index_name)
+ end
+
# Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps
#
# t.timestamps
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 17dd71e898..f1e42dfbbe 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -540,7 +540,7 @@ module ActiveRecord
column_type_sql << "(#{precision})"
end
elsif scale
- raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
end
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 76667616a1..e9677415cc 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -2,7 +2,7 @@ require 'active_record/connection_adapters/abstract_mysql_adapter'
require 'active_record/connection_adapters/statement_pool'
require 'active_support/core_ext/hash/keys'
-gem 'mysql', '~> 2.8.1'
+gem 'mysql', '~> 2.9'
require 'mysql'
class Mysql
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
index 62d091357d..c04a799b8d 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -8,6 +8,8 @@ module ActiveRecord
case string
when 'infinity'; 1.0 / 0.0
when '-infinity'; -1.0 / 0.0
+ when / BC$/
+ super("-" + string.sub(/ BC$/, ""))
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index 9d3fa18e3a..62a4d76928 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -129,11 +129,15 @@ module ActiveRecord
# Quote date/time values for use in SQL input. Includes microseconds
# if the value is a Time responding to usec.
def quoted_date(value) #:nodoc:
+ result = super
if value.acts_like?(:time) && value.respond_to?(:usec)
- "#{super}.#{sprintf("%06d", value.usec)}"
- else
- super
+ result = "#{result}.#{sprintf("%06d", value.usec)}"
+ end
+
+ if value.year < 0
+ result = result.sub(/^-/, "") + " BC"
end
+ result
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 82a0b662f4..7c561b6f82 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -396,6 +396,13 @@ module ActiveRecord
when nil, 0..0x3fffffff; super(type)
else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
end
+ when 'text'
+ # PostgreSQL doesn't support limits on text columns.
+ # The hard limit is 1Gb, according to section 8.3 in the manual.
+ case limit
+ when nil, 0..0x3fffffff; super(type)
+ else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
+ end
when 'integer'
return 'integer' unless limit
@@ -422,20 +429,17 @@ module ActiveRecord
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
# requires that the ORDER BY include the distinct column.
#
- # distinct("posts.id", "posts.created_at desc")
+ # distinct("posts.id", ["posts.created_at desc"])
+ # # => "DISTINCT posts.id, posts.created_at AS alias_0"
def distinct(columns, orders) #:nodoc:
- return "DISTINCT #{columns}" if orders.empty?
-
- # Construct a clean list of column names from the ORDER BY clause, removing
- # any ASC/DESC modifiers
- order_columns = orders.collect do |s|
- s = s.to_sql unless s.is_a?(String)
- s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '')
- end
- order_columns.delete_if { |c| c.blank? }
- order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
-
- "DISTINCT #{columns}, #{order_columns * ', '}"
+ order_columns = orders.map{ |s|
+ # Convert Arel node to string
+ s = s.to_sql unless s.is_a?(String)
+ # Remove any ASC/DESC modifiers
+ s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '')
+ }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
+
+ [super].concat(order_columns).join(', ')
end
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 957027c1ee..94c6684700 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -83,7 +83,12 @@ module ActiveRecord
@attribute_methods_mutex = Mutex.new
# force attribute methods to be higher in inheritance hierarchy than other generated methods
- generated_attribute_methods
+ generated_attribute_methods.const_set(:AttrNames, Module.new {
+ def self.const_missing(name)
+ const_set(name, [name.to_s.sub(/ATTR_/, '')].pack('h*').freeze)
+ end
+ })
+
generated_feature_methods
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 26ad2087c1..7922bbcfa0 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -381,6 +381,12 @@ module ActiveRecord
@@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
+ def self.find_table_name(fixture_set_name) # :nodoc:
+ ActiveSupport::Deprecation.warn(
+ "ActiveRecord::Fixtures.find_table_name is deprecated and shall be removed from future releases. Use ActiveRecord::Fixtures.default_fixture_model_name instead.")
+ default_fixture_model_name(fixture_set_name)
+ end
+
def self.default_fixture_model_name(fixture_set_name) # :nodoc:
ActiveRecord::Base.pluralize_table_names ?
fixture_set_name.singularize.camelize :
diff --git a/activerecord/lib/active_record/migration/join_table.rb b/activerecord/lib/active_record/migration/join_table.rb
index e880ae97bb..ebf64cbcdc 100644
--- a/activerecord/lib/active_record/migration/join_table.rb
+++ b/activerecord/lib/active_record/migration/join_table.rb
@@ -8,7 +8,7 @@ module ActiveRecord
end
def join_table_name(table_1, table_2)
- [table_1, table_2].sort.join("_").to_sym
+ [table_1.to_s, table_2.to_s].sort.join("_").to_sym
end
end
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 8e749772a1..eed49e17b1 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -72,11 +72,9 @@ module ActiveRecord
# +save+ returns +false+. See ActiveRecord::Callbacks for further
# details.
def save(*)
- begin
- create_or_update
- rescue ActiveRecord::RecordInvalid
- false
- end
+ create_or_update
+ rescue ActiveRecord::RecordInvalid
+ false
end
# Saves the model.
@@ -155,7 +153,18 @@ module ActiveRecord
became.instance_variable_set("@new_record", new_record?)
became.instance_variable_set("@destroyed", destroyed?)
became.instance_variable_set("@errors", errors)
- became.public_send("#{klass.inheritance_column}=", klass.name) unless self.class.descends_from_active_record?
+ became
+ end
+
+ # Wrapper around +becomes+ that also changes the instance's sti column value.
+ # This is especially useful if you want to persist the changed class in your
+ # database.
+ #
+ # Note: The old instance's sti column value will be changed too, as both objects
+ # share the same set of attributes.
+ def becomes!(klass)
+ became = becomes(klass)
+ became.public_send("#{klass.inheritance_column}=", klass.sti_name) unless self.class.descends_from_active_record?
became
end
@@ -171,7 +180,7 @@ module ActiveRecord
name = name.to_s
verify_readonly_attribute(name)
send("#{name}=", value)
- save(:validate => false)
+ save(validate: false)
end
# Updates the attributes of the model from the passed-in hash and saves the
@@ -226,8 +235,8 @@ module ActiveRecord
updated_count = self.class.where(self.class.primary_key => id).update_all(attributes)
- attributes.each do |k,v|
- raw_write_attribute(k,v)
+ attributes.each do |k, v|
+ raw_write_attribute(k, v)
end
updated_count == 1
@@ -379,10 +388,14 @@ module ActiveRecord
# Returns the number of affected rows.
def update(attribute_names = @attributes.keys)
attributes_with_values = arel_attributes_with_values_for_update(attribute_names)
- return 0 if attributes_with_values.empty?
- klass = self.class
- stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values)
- klass.connection.update stmt
+
+ if attributes_with_values.empty?
+ 0
+ else
+ klass = self.class
+ stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values)
+ klass.connection.update stmt
+ end
end
# Creates a record with values matching those of the instance attributes
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index c8edadf804..72b035a023 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/object/try'
+require 'active_support/deprecation'
module ActiveRecord
module Calculations
@@ -15,16 +15,9 @@ module ActiveRecord
#
# Person.count(:age, distinct: true)
# # => counts the number of different age values
- #
- # Person.where("age > 26").count { |person| person.gender == 'female' }
- # # => queries people where "age > 26" then count the loaded results filtering by gender
def count(column_name = nil, options = {})
- if block_given?
- self.to_a.count { |item| yield item }
- else
- column_name, options = nil, column_name if column_name.is_a?(Hash)
- calculate(:count, column_name, options)
- end
+ column_name, options = nil, column_name if column_name.is_a?(Hash)
+ calculate(:count, column_name, options)
end
# Calculates the average value on a given column. Returns +nil+ if there's
@@ -58,13 +51,13 @@ module ActiveRecord
# +calculate+ for examples with options.
#
# Person.sum('age') # => 4562
- # # => returns the total sum of all people's age
- #
- # Person.where('age > 100').sum { |person| person.age - 100 }
- # # queries people where "age > 100" then perform a sum calculation with the block returns
def sum(*args)
if block_given?
- self.to_a.sum(*args) { |item| yield item }
+ ActiveSupport::Deprecation.warn(
+ "Calling #sum with a block is deprecated and will be removed in Rails 4.1. " \
+ "If you want to perform sum calculation over the array of elements, use `to_a.sum(&block)`."
+ )
+ self.to_a.sum(*args) {|*block_args| yield(*block_args)}
else
calculate(:sum, *args)
end
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index f1362dd15f..872204c644 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -216,6 +216,35 @@ module ActiveRecord
assert_equal "(number > 100)", index.where
end
+ def test_distinct_zero_orders
+ assert_equal "DISTINCT posts.id",
+ @connection.distinct("posts.id", [])
+ end
+
+ def test_distinct_one_order
+ assert_equal "DISTINCT posts.id, posts.created_at AS alias_0",
+ @connection.distinct("posts.id", ["posts.created_at desc"])
+ end
+
+ def test_distinct_few_orders
+ assert_equal "DISTINCT posts.id, posts.created_at AS alias_0, posts.position AS alias_1",
+ @connection.distinct("posts.id", ["posts.created_at desc", "posts.position asc"])
+ end
+
+ def test_distinct_blank_not_nil_orders
+ assert_equal "DISTINCT posts.id, posts.created_at AS alias_0",
+ @connection.distinct("posts.id", ["posts.created_at desc", "", " "])
+ end
+
+ def test_distinct_with_arel_order
+ order = Object.new
+ def order.to_sql
+ "posts.created_at desc"
+ end
+ assert_equal "DISTINCT posts.id, posts.created_at AS alias_0",
+ @connection.distinct("posts.id", [order])
+ end
+
def test_distinct_with_nulls
assert_equal "DISTINCT posts.title, posts.updater_id AS alias_0", @connection.distinct("posts.title", ["posts.updater_id desc nulls first"])
assert_equal "DISTINCT posts.title, posts.updater_id AS alias_0", @connection.distinct("posts.title", ["posts.updater_id desc nulls last"])
diff --git a/activerecord/test/cases/adapters/postgresql/sql_types_test.rb b/activerecord/test/cases/adapters/postgresql/sql_types_test.rb
new file mode 100644
index 0000000000..d7d40f6385
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/sql_types_test.rb
@@ -0,0 +1,18 @@
+require "cases/helper"
+
+class SqlTypesTest < ActiveRecord::TestCase
+ def test_binary_types
+ assert_equal 'bytea', type_to_sql(:binary, 100_000)
+ assert_raise ActiveRecord::ActiveRecordError do
+ type_to_sql :binary, 4294967295
+ end
+ assert_equal 'text', type_to_sql(:text, 100_000)
+ assert_raise ActiveRecord::ActiveRecordError do
+ type_to_sql :text, 4294967295
+ end
+ end
+
+ def type_to_sql(*args)
+ ActiveRecord::Base.connection.type_to_sql(*args)
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
index 26507ad654..630bdeec67 100644
--- a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
@@ -75,6 +75,15 @@ class TimestampTest < ActiveRecord::TestCase
assert_equal '4', pg_datetime_precision('foos', 'updated_at')
end
+ def test_bc_timestamp
+ unless current_adapter?(:PostgreSQLAdapter)
+ return skip("only tested on postgresql")
+ end
+ date = Date.new(0) - 1.second
+ Developer.create!(:name => "aaron", :updated_at => date)
+ assert_equal date, Developer.find_by_name("aaron").updated_at
+ end
+
private
def pg_datetime_precision(table_name, column_name)
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index b1066fb24c..2ded97582d 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1150,6 +1150,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert companies(:first_firm).clients.include?(Client.find(2))
end
+ def test_included_in_collection_for_new_records
+ client = Client.create(:name => 'Persisted')
+ assert_nil client.client_of
+ assert !Firm.new.clients_of_firm.include?(client),
+ 'includes a client that does not belong to any firm'
+ end
+
def test_adding_array_and_collection
assert_nothing_raised { Firm.first.clients + Firm.all.last.clients }
end
@@ -1572,6 +1579,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [tagging], post.taggings
end
+ def test_build_with_polymotphic_has_many_does_not_allow_to_override_type_and_id
+ welcome = posts(:welcome)
+ tagging = welcome.taggings.build(:taggable_id => 99, :taggable_type => 'ShouldNotChange')
+
+ assert_equal welcome.id, tagging.taggable_id
+ assert_equal 'Post', tagging.taggable_type
+ end
+
def test_dont_call_save_callbacks_twice_on_has_many
firm = companies(:first_firm)
contract = firm.contracts.create!
@@ -1651,6 +1666,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_deprecated { klass.has_many :foo, :counter_sql => 'lol' }
end
+ test "sum calculation with block for array compatibility is deprecated" do
+ assert_deprecated do
+ posts(:welcome).comments.sum { |c| c.id }
+ end
+ end
+
test "has many associations on new records use null relations" do
post = Post.new
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 8b82b79219..c503c21e27 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -12,6 +12,8 @@ require 'models/contact'
require 'models/keyboard'
class AttributeMethodsTest < ActiveRecord::TestCase
+ include InTimeZone
+
fixtures :topics, :developers, :companies, :computers
def setup
@@ -311,26 +313,17 @@ class AttributeMethodsTest < ActiveRecord::TestCase
def test_read_write_boolean_attribute
topic = Topic.new
- # puts ""
- # puts "New Topic"
- # puts topic.inspect
topic.approved = "false"
- # puts "Expecting false"
- # puts topic.inspect
assert !topic.approved?, "approved should be false"
+
topic.approved = "false"
- # puts "Expecting false"
- # puts topic.inspect
assert !topic.approved?, "approved should be false"
+
topic.approved = "true"
- # puts "Expecting true"
- # puts topic.inspect
assert topic.approved?, "approved should be true"
+
topic.approved = "true"
- # puts "Expecting true"
- # puts topic.inspect
assert topic.approved?, "approved should be true"
- # puts ""
end
def test_overridden_write_attribute
@@ -793,27 +786,13 @@ class AttributeMethodsTest < ActiveRecord::TestCase
private
def cached_columns
- Topic.columns.find_all { |column|
- !Topic.serialized_attributes.include? column.name
- }.map(&:name)
+ Topic.columns.map(&:name) - Topic.serialized_attributes.keys
end
def time_related_columns_on_topic
Topic.columns.select { |c| [:time, :date, :datetime, :timestamp].include?(c.type) }
end
- def in_time_zone(zone)
- old_zone = Time.zone
- old_tz = ActiveRecord::Base.time_zone_aware_attributes
-
- Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
- ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
- yield
- ensure
- Time.zone = old_zone
- ActiveRecord::Base.time_zone_aware_attributes = old_tz
- end
-
def privatize(method_signature)
@target.class_eval(<<-private_method, __FILE__, __LINE__ + 1)
private
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 65d28ea028..5cb7eabf0e 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -383,30 +383,16 @@ class CalculationsTest < ActiveRecord::TestCase
Company.where(:type => "Firm").from('companies').count(:type)
end
- def test_count_with_block_acts_as_array
- accounts = Account.where('id > 0')
- assert_equal Account.count, accounts.count { true }
- assert_equal 0, accounts.count { false }
- assert_equal Account.where('credit_limit > 50').size, accounts.count { |account| account.credit_limit > 50 }
- assert_equal Account.count, Account.count { true }
- assert_equal 0, Account.count { false }
- end
-
- def test_sum_with_block_acts_as_array
- accounts = Account.where('id > 0')
- assert_equal Account.sum(:credit_limit), accounts.sum { |account| account.credit_limit }
- assert_equal Account.sum(:credit_limit) + Account.count, accounts.sum{ |account| account.credit_limit + 1 }
- assert_equal 0, accounts.sum { |account| 0 }
- end
-
def test_sum_with_from_option
assert_equal Account.sum(:credit_limit), Account.from('accounts').sum(:credit_limit)
assert_equal Account.where("credit_limit > 50").sum(:credit_limit),
Account.where("credit_limit > 50").from('accounts').sum(:credit_limit)
end
- def test_sum_array_compatibility
- assert_equal Account.sum(:credit_limit), Account.sum(&:credit_limit)
+ def test_sum_array_compatibility_deprecation
+ assert_deprecated do
+ assert_equal Account.sum(:credit_limit), Account.sum(&:credit_limit)
+ end
end
def test_average_with_from_option
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 40f1dbccde..d4fc5f204b 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -27,6 +27,8 @@ class NumericData < ActiveRecord::Base
end
class DirtyTest < ActiveRecord::TestCase
+ include InTimeZone
+
# Dummy to force column loads so query counts are clean.
def setup
Person.create :first_name => 'foo'
@@ -121,7 +123,6 @@ class DirtyTest < ActiveRecord::TestCase
end
def test_time_attributes_changes_without_time_zone
-
target = Class.new(ActiveRecord::Base)
target.table_name = 'pirates'
@@ -604,16 +605,4 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal %w(parrot_id), pirate.changed
assert_nil pirate.parrot_id_was
end
-
- def in_time_zone(zone)
- old_zone = Time.zone
- old_tz = ActiveRecord::Base.time_zone_aware_attributes
-
- Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
- ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
- yield
- ensure
- Time.zone = old_zone
- ActiveRecord::Base.time_zone_aware_attributes = old_tz
- end
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index cff6689c15..1bff005510 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -135,3 +135,18 @@ module LogIntercepter
end
end
+module InTimeZone
+ private
+
+ def in_time_zone(zone)
+ old_zone = Time.zone
+ old_tz = ActiveRecord::Base.time_zone_aware_attributes
+
+ Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
+ ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
+ yield
+ ensure
+ Time.zone = old_zone
+ ActiveRecord::Base.time_zone_aware_attributes = old_tz
+ end
+end
diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb
index 4614be9650..8fb03cdee0 100644
--- a/activerecord/test/cases/migration/change_table_test.rb
+++ b/activerecord/test/cases/migration/change_table_test.rb
@@ -164,6 +164,13 @@ module ActiveRecord
end
end
+ def test_rename_index_renames_index
+ with_change_table do |t|
+ @connection.expect :rename_index, nil, [:delete_me, :bar, :baz]
+ t.rename_index :bar, :baz
+ end
+ end
+
def test_change_changes_column
with_change_table do |t|
@connection.expect :change_column, nil, [:delete_me, :bar, :string, {}]
diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb
index cd1b0e8b47..f262bbaad7 100644
--- a/activerecord/test/cases/migration/create_join_table_test.rb
+++ b/activerecord/test/cases/migration/create_join_table_test.rb
@@ -35,6 +35,12 @@ module ActiveRecord
assert_equal %w(artist_id music_id), connection.columns(:artists_musics).map(&:name).sort
end
+ def test_create_join_table_with_symbol_and_string
+ connection.create_join_table :artists, 'musics'
+
+ assert_equal %w(artist_id music_id), connection.columns(:artists_musics).map(&:name).sort
+ end
+
def test_create_join_table_with_the_proper_order
connection.create_join_table :videos, :musics
diff --git a/activerecord/test/cases/migration/helper.rb b/activerecord/test/cases/migration/helper.rb
index 768ebc5861..e28feedcf9 100644
--- a/activerecord/test/cases/migration/helper.rb
+++ b/activerecord/test/cases/migration/helper.rb
@@ -2,12 +2,10 @@ require "cases/helper"
module ActiveRecord
class Migration
- class << self
- attr_accessor :message_count
- end
+ class << self; attr_accessor :message_count; end
+ self.message_count = 0
def puts(text="")
- ActiveRecord::Migration.message_count ||= 0
ActiveRecord::Migration.message_count += 1
end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 4b938da5c4..b2609f6395 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -280,12 +280,23 @@ class PersistencesTest < ActiveRecord::TestCase
def test_update_sti_type
assert_instance_of Reply, topics(:second)
- topic = topics(:second).becomes(Topic)
+ topic = topics(:second).becomes!(Topic)
assert_instance_of Topic, topic
topic.save!
assert_instance_of Topic, Topic.find(topic.id)
end
+ def test_preserve_original_sti_type
+ reply = topics(:second)
+ assert_equal "Reply", reply.type
+
+ topic = reply.becomes(Topic)
+ assert_equal "Reply", reply.type
+
+ assert_instance_of Topic, topic
+ assert_equal "Reply", topic.type
+ end
+
def test_delete
topic = Topic.find(1)
assert_equal topic, topic.delete, 'topic.delete did not return self'
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index bc6cac0c6c..c34aeaf925 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -157,19 +157,19 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 4, topics.to_a.size
assert_equal topics(:first).title, topics.first.title
end
-
+
def test_finding_with_assoc_order
topics = Topic.order(:id => :desc)
assert_equal 4, topics.to_a.size
assert_equal topics(:fourth).title, topics.first.title
end
-
+
def test_finding_with_reverted_assoc_order
topics = Topic.order(:id => :asc).reverse_order
assert_equal 4, topics.to_a.size
assert_equal topics(:fourth).title, topics.first.title
end
-
+
def test_raising_exception_on_invalid_hash_params
assert_raise(ArgumentError) { Topic.order(:name, "id DESC", :id => :DeSc) }
end
@@ -1438,4 +1438,18 @@ class RelationTest < ActiveRecord::TestCase
end
assert_no_queries { relation.to_a }
end
+
+ test 'group with select and includes' do
+ authors_count = Post.select('author_id, COUNT(author_id) AS num_posts').
+ group('author_id').order('author_id').includes(:author).to_a
+
+ assert_no_queries do
+ result = authors_count.map do |post|
+ [post.num_posts, post.author.try(:name)]
+ end
+
+ expected = [[1, nil], [5, "David"], [3, "Mary"], [2, "Bob"]]
+ assert_equal expected, result
+ end
+ end
end
diff --git a/activerecord/test/models/reference.rb b/activerecord/test/models/reference.rb
index 561b431766..1636c118a2 100644
--- a/activerecord/test/models/reference.rb
+++ b/activerecord/test/models/reference.rb
@@ -4,9 +4,8 @@ class Reference < ActiveRecord::Base
has_many :agents_posts_authors, :through => :person
- class << self
- attr_accessor :make_comments
- end
+ class << self; attr_accessor :make_comments; end
+ self.make_comments = false
before_destroy :make_comments
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index d0e7338f15..0cfde83778 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -192,5 +192,10 @@ end
_SQL
rescue #This version of PostgreSQL either has no XML support or is was not compiled with XML support: skipping table
end
+
+ create_table :limitless_fields, force: true do |t|
+ t.binary :binary, limit: 100_000
+ t.text :text, limit: 100_000
+ end
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index b55a706b2f..504ebcb2fe 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,7 @@
## Rails 4.0.0 (unreleased) ##
+* Deprecate `ActiveSupport::TestCase#pending` method, use `skip` from MiniTest instead. *Carlos Antonio da Silva*
+
* `XmlMini.with_backend` now may be safely used with threads:
Thread.new do
@@ -270,8 +272,6 @@
* Add html_escape_once to ERB::Util, and delegate escape_once tag helper to it. *Carlos Antonio da Silva*
-* Remove ActiveSupport::TestCase#pending method, use `skip` instead. *Carlos Antonio da Silva*
-
* Deprecates the compatibility method Module#local_constant_names,
use Module#local_constants instead (which returns symbols). *fxn*
diff --git a/activesupport/lib/active_support/core_ext/hash/diff.rb b/activesupport/lib/active_support/core_ext/hash/diff.rb
index a27c55479a..5f3868b5b0 100644
--- a/activesupport/lib/active_support/core_ext/hash/diff.rb
+++ b/activesupport/lib/active_support/core_ext/hash/diff.rb
@@ -6,7 +6,7 @@ class Hash
# {}.diff(1 => 2) # => {1 => 2}
# {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4}
def diff(other)
- ActiveSupport::Deprecation.warn "Hash#diff is no longer used inside of Rails, and is being deprecated with no replacement. If you're using it to compare hashes for the purpose of testing, please use MiniTest's diff instead."
+ ActiveSupport::Deprecation.warn "Hash#diff is no longer used inside of Rails, and is being deprecated with no replacement. If you're using it to compare hashes for the purpose of testing, please use MiniTest's assert_equal instead."
dup.
delete_if { |k, v| other[k] == v }.
merge!(other.dup.delete_if { |k, v| has_key?(k) })
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index c96ad17aba..ee7bda9f93 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -4,6 +4,7 @@ require 'active_support/testing/tagged_logging'
require 'active_support/testing/setup_and_teardown'
require 'active_support/testing/assertions'
require 'active_support/testing/deprecation'
+require 'active_support/testing/pending'
require 'active_support/testing/isolation'
require 'active_support/testing/constant_lookup'
require 'active_support/core_ext/kernel/reporting'
@@ -40,6 +41,7 @@ module ActiveSupport
include ActiveSupport::Testing::SetupAndTeardown
include ActiveSupport::Testing::Assertions
include ActiveSupport::Testing::Deprecation
+ include ActiveSupport::Testing::Pending
def self.describe(text)
if block_given?
diff --git a/activesupport/lib/active_support/testing/pending.rb b/activesupport/lib/active_support/testing/pending.rb
new file mode 100644
index 0000000000..944806bb64
--- /dev/null
+++ b/activesupport/lib/active_support/testing/pending.rb
@@ -0,0 +1,14 @@
+require 'active_support/deprecation'
+
+module ActiveSupport
+ module Testing
+ module Pending
+ unless defined?(Spec)
+ def pending(description = "", &block)
+ ActiveSupport::Deprecation.warn("#pending is deprecated and will be removed in Rails 4.1, please use #skip instead.")
+ skip(description.blank? ? nil : description)
+ end
+ end
+ end
+ end
+end
diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb
index 64426d02e9..dfe9f3c11c 100644
--- a/activesupport/test/test_case_test.rb
+++ b/activesupport/test/test_case_test.rb
@@ -108,5 +108,11 @@ module ActiveSupport
test = tc.new test_name
assert_raises(Interrupt) { test.run fr }
end
+
+ def test_pending_deprecation
+ assert_deprecated do
+ pending "should use #skip instead"
+ end
+ end
end
end
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index fb26a3a6a3..8687cfea52 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -377,23 +377,7 @@ If you use this setting, you should pass the `only_path: false` option when usin
Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have `welcome_email.text.erb` and `welcome_email.html.erb` in `app/views/user_mailer`, Action Mailer will automatically send a multipart email with the HTML and text versions setup as different parts.
-The order of the parts getting inserted is determined by the `:parts_order` inside of the `ActionMailer::Base.default` method. If you want to explicitly alter the order, you can either change the `:parts_order` or explicitly render the parts in a different order:
-
-```ruby
-class UserMailer < ActionMailer::Base
- def welcome_email(user)
- @user = user
- @url = user_url(@user)
- mail(to: user.email,
- subject: 'Welcome to My Awesome Site') do |format|
- format.html
- format.text
- end
- end
-end
-```
-
-Will put the HTML part first, and the plain text part second.
+The order of the parts getting inserted is determined by the `:parts_order` inside of the `ActionMailer::Base.default` method.
### Sending Emails with Attachments
diff --git a/guides/source/active_record_validations_callbacks.md b/guides/source/active_record_validations_callbacks.md
index 5c27ccbf9e..0f4140b650 100644
--- a/guides/source/active_record_validations_callbacks.md
+++ b/guides/source/active_record_validations_callbacks.md
@@ -953,8 +953,12 @@ Below is a simple example where we change the Rails behavior to always display t
```ruby
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
- errors = Array(instance.error_message).join(',')
- %(#{html_tag}<span class="validation-error">&nbsp;#{errors}</span>).html_safe
+ if html_tag =~ /\<label/
+ html_tag
+ else
+ errors = Array(instance.error_message).join(',')
+ %(#{html_tag}<span class="validation-error">&nbsp;#{errors}</span>).html_safe
+ end
end
```
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 6fb10693ff..bf81776006 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -69,6 +69,8 @@ in the `config/initializers/wrap_parameters.rb` file:
### Action Pack
+There is an upgrading cookie store UpgradeSignatureToEncryptionCookieStore which helps you upgrading apps that use +CookieStore+ to the new default +EncryptedCookieStore+. To use this CookieStore set Myapp::Application.config.session_store :upgrade_signature_to_encryption_cookie_store, key: '_myapp_session' in your config/initializers/session_store.rb. You will also need to add Myapp::Application.config.secret_key_base = 'some secret' in your config/initializers/secret_token.rb, but do not remove +Myapp::Application.config.secret_token = 'some secret'+
+
Rails 4.0 removed the `ActionController::Base.asset_path` option. Use the assets pipeline feature.
Rails 4.0 has deprecated `ActionController::Base.page_cache_extension` option. Use
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index a0545916ca..7a68cf0a2e 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,5 +1,15 @@
## Rails 4.0.0 (unreleased) ##
+* Engines with a dummy app include the rake tasks of dependencies in the app namespace.
+ Fix #8229
+
+ *Yves Senn*
+
+* Add sqlserver.yml template file to satisfy '-d sqlserver' being passed to 'rails new'.
+ Fix #6882
+
+ *Robert Nesius*
+
* Rake test:uncommitted finds git directory in ancestors *Nicolas Despres*
* Add dummy app Rake tasks when --skip-test-unit and --dummy-path is passed to the plugin generator.
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index f97e66985c..89afeaeec5 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -105,6 +105,10 @@ module Rails
def database_configuration
require 'erb'
YAML.load ERB.new(IO.read(paths["config/database"].first)).result
+ rescue Psych::SyntaxError => e
+ raise "YAML syntax error occurred while parsing #{paths["config/database"].first}. " \
+ "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
+ "Error: #{e.message}"
end
def log_level
diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb
index 90359d1c08..4a5674236d 100644
--- a/railties/lib/rails/commands/dbconsole.rb
+++ b/railties/lib/rails/commands/dbconsole.rb
@@ -5,8 +5,8 @@ require 'rbconfig'
module Rails
class DBConsole
- attr_reader :config, :arguments
-
+ attr_reader :arguments
+
def self.start
new.start
end
@@ -59,7 +59,7 @@ module Rails
args << "-#{options['mode']}" if options['mode']
args << "-header" if options['header']
- args << File.expand_path(config['database'], Rails.root)
+ args << File.expand_path(config['database'], Rails.respond_to?(:root) ? Rails.root : nil)
find_cmd_and_exec('sqlite3', *args)
@@ -82,17 +82,13 @@ module Rails
def config
@config ||= begin
cfg = begin
- cfg = YAML.load(ERB.new(IO.read("config/database.yml")).result)
+ YAML.load(ERB.new(IO.read("config/database.yml")).result)
rescue SyntaxError, StandardError
require APP_PATH
Rails.application.config.database_configuration
end
- unless cfg[environment]
- abort "No database is configured for the environment '#{environment}'"
- end
-
- cfg[environment]
+ cfg[environment] || abort("No database is configured for the environment '#{environment}'")
end
end
@@ -108,7 +104,7 @@ module Rails
def parse_arguments(arguments)
options = {}
-
+
OptionParser.new do |opt|
opt.banner = "Usage: rails dbconsole [environment] [options]"
opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v|
@@ -123,7 +119,7 @@ module Rails
opt.on("--header") do |h|
options['header'] = h
end
-
+
opt.on("-h", "--help", "Show this help message.") do
puts opt
exit
@@ -142,7 +138,7 @@ module Rails
env = arguments.first
options[:environment] = %w(production development test).detect {|e| e =~ /^#{env}/} || env
end
-
+
options
end
diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb
index 0cc672e01c..62d82cc005 100644
--- a/railties/lib/rails/commands/runner.rb
+++ b/railties/lib/rails/commands/runner.rb
@@ -41,7 +41,7 @@ ENV["RAILS_ENV"] = options[:environment]
require APP_PATH
Rails.application.require_environment!
- Rails.application.load_runner
+Rails.application.load_runner
if code_or_file.nil?
$stderr.puts "Run '#{$0} -h' for help."
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 2c2bb1c714..f6721c617f 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -407,6 +407,8 @@ module Rails
end
end
+ self.isolated = false
+
delegate :middleware, :root, :paths, to: :config
delegate :engine_name, :isolated?, to: "self.class"
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
new file mode 100644
index 0000000000..b52b733c56
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
@@ -0,0 +1,57 @@
+# SQL Server (2005 or higher recommended)
+#
+# Install the adapters and driver
+# gem install tiny_tds
+# gem install activerecord-sqlserver-adapter
+#
+# Ensure the activerecord adapter and db driver gems are defined in your Gemfile
+# gem 'tiny_tds'
+# gem 'activerecord-sqlserver-adapter'
+#
+# You should make sure freetds is configured correctly first.
+# freetds.conf contains host/port/protocol_versions settings.
+# http://freetds.schemamania.org/userguide/freetdsconf.htm
+#
+# A typical Microsoft server
+# [mssql]
+# host = mssqlserver.yourdomain.com
+# port = 1433
+# tds version = 7.1
+
+# If you can connect with "tsql -S servername", your basic FreeTDS installation is working.
+# 'man tsql' for more info
+# Set timeout to a larger number if valid queries against a live db fail
+
+development:
+ adapter: sqlserver
+ encoding: utf8
+ reconnect: false
+ database: <%= app_name %>_development
+ username: <%= app_name %>
+ password:
+ timeout: 25
+ dataserver: from_freetds.conf
+
+
+# Warning: The database defined as "test" will be erased and
+# re-generated from your development database when you run "rake".
+# Do not set this db to the same as development or production.
+test:
+ adapter: sqlserver
+ encoding: utf8
+ reconnect: false
+ database: <%= app_name %>_test
+ username: <%= app_name %>
+ password:
+ timeout: 25
+ dataserver: from_freetds.conf
+
+production:
+ adapter: sqlserver
+ encoding: utf8
+ reconnect: false
+ database: <%= app_name %>_production
+ username: <%= app_name %>
+ password:
+ timeout: 25
+ dataserver: from_freetds.conf
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb b/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb
index 8a8ba04a70..310c975262 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb
@@ -11,7 +11,7 @@ require "action_mailer/railtie"
<%= comment_if :skip_test_unit %>require "rails/test_unit/railtie"
<% end -%>
-Bundler.require
+Bundler.require(*Rails.groups)
require "<%= name %>"
<%= application_definition %>
diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb
index d45bdaabf5..6316584825 100644
--- a/railties/test/commands/dbconsole_test.rb
+++ b/railties/test/commands/dbconsole_test.rb
@@ -116,6 +116,14 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
assert !aborted
end
+ def test_sqlite3_db_without_defined_rails_root
+ Rails.stubs(:respond_to?)
+ Rails.expects(:respond_to?).with(:root).once.returns(false)
+ dbconsole.expects(:find_cmd_and_exec).with('sqlite3', Rails.root.join('../config/db.sqlite3').to_s)
+ start(adapter: 'sqlite3', database: 'config/db.sqlite3')
+ assert !aborted
+ end
+
def test_oracle
dbconsole.expects(:find_cmd_and_exec).with('sqlplus', 'user@db')
start(adapter: 'oracle', database: 'db', username: 'user', password: 'secret')