aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/CHANGELOG.md49
-rw-r--r--actionpack/lib/action_controller/base.rb2
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb1
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb18
-rw-r--r--actionpack/test/controller/mime/respond_with_test.rb15
-rw-r--r--actionpack/test/dispatch/prefix_generation_test.rb100
-rw-r--r--actionpack/test/fixtures/respond_with/respond_with_additional_params.html.erb0
-rw-r--r--actionview/lib/action_view/helpers/date_helper.rb4
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb2
-rw-r--r--actionview/test/actionpack/controller/view_paths_test.rb (renamed from actionview/test/lib/controller/view_paths_test.rb)0
-rw-r--r--actionview/test/lib/controller/fake_controllers.rb35
-rw-r--r--activemodel/CHANGELOG.md4
-rw-r--r--activemodel/activemodel.gemspec2
-rw-r--r--activemodel/examples/validations.rb1
-rw-r--r--activemodel/lib/active_model/secure_password.rb2
-rw-r--r--activemodel/test/cases/secure_password_test.rb8
-rw-r--r--activerecord/CHANGELOG.md12
-rw-r--r--activerecord/lib/active_record/associations/builder/association.rb61
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb10
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb31
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb10
-rw-r--r--activerecord/lib/active_record/associations/builder/has_many.rb4
-rw-r--r--activerecord/lib/active_record/associations/builder/has_one.rb6
-rw-r--r--activerecord/lib/active_record/associations/builder/singular_association.rb11
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb222
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_association.rb43
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_base.rb10
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_part.rb33
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb6
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb2
-rw-r--r--activerecord/lib/active_record/callbacks.rb5
-rw-r--r--activerecord/lib/active_record/inheritance.rb2
-rw-r--r--activerecord/lib/active_record/persistence.rb3
-rw-r--r--activerecord/lib/active_record/reflection.rb25
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb4
-rw-r--r--activerecord/lib/active_record/relation/merger.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb9
-rw-r--r--activerecord/lib/active_record/result.rb4
-rw-r--r--activerecord/test/cases/associations/extension_test.rb3
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb2
-rw-r--r--activerecord/test/cases/relations_test.rb8
-rw-r--r--guides/source/action_mailer_basics.md2
-rw-r--r--guides/source/action_view_overview.md4
-rw-r--r--guides/source/active_record_basics.md2
-rw-r--r--guides/source/active_record_validations.md4
-rw-r--r--guides/source/command_line.md11
-rw-r--r--guides/source/getting_started.md4
-rw-r--r--guides/source/initialization.md22
-rw-r--r--guides/source/plugins.md12
-rw-r--r--guides/source/rails_on_rack.md6
-rw-r--r--railties/CHANGELOG.md4
-rw-r--r--railties/lib/rails/cli.rb1
-rw-r--r--railties/lib/rails/commands/server.rb3
-rw-r--r--railties/lib/rails/configuration.rb4
-rw-r--r--railties/test/application/middleware_test.rb6
-rw-r--r--railties/test/commands/server_test.rb60
-rw-r--r--railties/test/env_helpers.rb4
57 files changed, 563 insertions, 357 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index dc3e6d5c6a..9fb914ac40 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,13 +1,40 @@
+* Respect `SCRIPT_NAME` when using `redirect` with a relative path
+
+ Example:
+ # application routes.rb
+ mount BlogEngine => '/blog'
+
+ # engine routes.rb
+ get '/admin' => redirect('admin/dashboard')
+
+ This now redirects to the path `/blog/admin/dashboard`, whereas before it would've
+ generated an invalid url because there would be no slash between the host name and
+ the path. It also allows redirects to work where the application is deployed to a
+ subdirectory of a website.
+
+ Fixes #7977
+
+ *Andrew White*
+
+* Fixing repond_with working directly on the options hash
+ This fixes an issue where the respond_with worked directly with the given
+ options hash, so that if a user relied on it after calling respond_with,
+ the hash wouldn't be the same.
+
+ Fixes #12029.
+
+ *bluehotdog*
+
* Fix `ActionDispatch::RemoteIp::GetIp#calculate_ip` to only check for spoofing
attacks if both `HTTP_CLIENT_IP` and `HTTP_X_FORWARDED_FOR` are set.
- Fixes #10844
+ Fixes #10844.
*Tamir Duberstein*
* Strong parameters should permit nested number as key.
- Fixes #12293
+ Fixes #12293.
*kennyj*
@@ -23,14 +50,14 @@
* Fix an issue where router can't recognize downcased url encoding path.
- Fixes #12269
+ Fixes #12269.
*kennyj*
* Fix custom flash type definition. Misusage of the `_flash_types` class variable
caused an error when reloading controllers with custom flash types.
- Fixes #12057
+ Fixes #12057.
*Ricardo de Cillo*
@@ -51,21 +78,21 @@
* Fix an issue where :if and :unless controller action procs were being run
before checking for the correct action in the :only and :unless options.
- Fixes #11799
+ Fixes #11799.
*Nicholas Jakobsen*
* Fix an issue where `assert_dom_equal` and `assert_dom_not_equal` were
ignoring the passed failure message argument.
- Fixes #11751
+ Fixes #11751.
*Ryan McGeary*
* Allow REMOTE_ADDR, HTTP_HOST and HTTP_USER_AGENT to be overridden from
the environment passed into `ActionDispatch::TestRequest.new`.
- Fixes #11590
+ Fixes #11590.
*Andrew White*
@@ -80,7 +107,7 @@
* Skip routes pointing to a redirect or mounted application when generating urls
using an options hash as they aren't relevant and generate incorrect urls.
- Fixes #8018
+ Fixes #8018.
*Andrew White*
@@ -98,7 +125,7 @@
* Fix `ActionDispatch::ParamsParser#parse_formatted_parameters` to rewind body input stream on
parsing json params.
- Fixes #11345
+ Fixes #11345.
*Yuri Bol*, *Paul Nikitochkin*
@@ -131,7 +158,7 @@
was setting `request.formats` with an array containing a `nil` value, which
raised an error when setting the controller formats.
- Fixes #10965
+ Fixes #10965.
*Becker*
@@ -140,7 +167,7 @@
no `:to` present in the options hash so should only affect routes using the
shorthand syntax (i.e. endpoint is inferred from the path).
- Fixes #9856
+ Fixes #9856.
*Yves Senn*, *Andrew White*
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index db7b56f47e..3b0d094f4f 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -73,7 +73,7 @@ module ActionController
# <input type="text" name="post[address]" value="hyacintvej">
#
# A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
- # If the address input had been named \"post[address][street]", the params would have included
+ # If the address input had been named <tt>post[address][street]</tt>, the params would have included
# <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
#
# == Sessions
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 66dabd821f..a072fce1a1 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -326,6 +326,7 @@ module ActionController #:nodoc:
if collector = retrieve_collector_from_mimes(&block)
options = resources.size == 1 ? {} : resources.extract_options!
+ options = options.clone
options[:default_response] = collector.response
(options.delete(:responder) || self.class.responder).call(self, resources, options)
end
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index 68094f129f..3e54c7e71c 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -30,6 +30,10 @@ module ActionDispatch
uri.host ||= req.host
uri.port ||= req.port unless req.standard_port?
+ if relative_path?(uri.path)
+ uri.path = "#{req.script_name}/#{uri.path}"
+ end
+
body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
headers = {
@@ -48,6 +52,11 @@ module ActionDispatch
def inspect
"redirect(#{status})"
end
+
+ private
+ def relative_path?(path)
+ path && !path.empty? && path[0] != '/'
+ end
end
class PathRedirect < Redirect
@@ -81,6 +90,11 @@ module ActionDispatch
url_options[:path] = (url_options[:path] % escape_path(params))
end
+ if relative_path?(url_options[:path])
+ url_options[:path] = "/#{url_options[:path]}"
+ url_options[:script_name] = request.script_name
+ end
+
ActionDispatch::Http::URL.url_for url_options
end
@@ -104,6 +118,10 @@ module ActionDispatch
#
# get 'docs/:article', to: redirect('/wiki/%{article}')
#
+ # Note that if you return a path without a leading slash then the url is prefixed with the
+ # current SCRIPT_NAME environment variable. This is typically '/' but may be different in
+ # a mounted engine or where the application is deployed to a subdirectory of a website.
+ #
# Alternatively you can use one of the other syntaxes:
#
# The block version of redirect allows for the easy encapsulation of any logic associated with
diff --git a/actionpack/test/controller/mime/respond_with_test.rb b/actionpack/test/controller/mime/respond_with_test.rb
index 76af9e3414..a70592fa1b 100644
--- a/actionpack/test/controller/mime/respond_with_test.rb
+++ b/actionpack/test/controller/mime/respond_with_test.rb
@@ -65,7 +65,17 @@ class RespondWithController < ActionController::Base
respond_with(resource, :responder => responder)
end
+ def respond_with_additional_params
+ @params = RespondWithController.params
+ respond_with({:result => resource}, @params)
+ end
+
protected
+ def self.params
+ {
+ :foo => 'bar'
+ }
+ end
def resource
Customer.new("david", request.delete? ? nil : 13)
@@ -145,6 +155,11 @@ class RespondWithControllerTest < ActionController::TestCase
Mime::Type.unregister(:mobile)
end
+ def test_respond_with_shouldnt_modify_original_hash
+ get :respond_with_additional_params
+ assert_equal RespondWithController.params, assigns(:params)
+ end
+
def test_using_resource
@request.accept = "application/xml"
get :using_resource
diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb
index 113608ecf4..e519fff51e 100644
--- a/actionpack/test/dispatch/prefix_generation_test.rb
+++ b/actionpack/test/dispatch/prefix_generation_test.rb
@@ -31,6 +31,14 @@ module TestGenerationPrefix
get "/polymorphic_path_for_engine", :to => "inside_engine_generating#polymorphic_path_for_engine"
get "/conflicting_url", :to => "inside_engine_generating#conflicting"
get "/foo", :to => "never#invoked", :as => :named_helper_that_should_be_invoked_only_in_respond_to_test
+
+ get "/relative_path_redirect", :to => redirect("foo")
+ get "/relative_option_redirect", :to => redirect(:path => "foo")
+ get "/relative_custom_redirect", :to => redirect { |params, request| "foo" }
+
+ get "/absolute_path_redirect", :to => redirect("/foo")
+ get "/absolute_option_redirect", :to => redirect(:path => "/foo")
+ get "/absolute_custom_redirect", :to => redirect { |params, request| "/foo" }
end
routes
@@ -182,6 +190,48 @@ module TestGenerationPrefix
assert_equal "engine", last_response.body
end
+ test "[ENGINE] relative path redirect uses SCRIPT_NAME from request" do
+ get "/awesome/blog/relative_path_redirect"
+ assert_equal 301, last_response.status
+ assert_equal "http://example.org/awesome/blog/foo", last_response.headers["Location"]
+ assert_equal %(<html><body>You are being <a href="http://example.org/awesome/blog/foo">redirected</a>.</body></html>), last_response.body
+ end
+
+ test "[ENGINE] relative option redirect uses SCRIPT_NAME from request" do
+ get "/awesome/blog/relative_option_redirect"
+ assert_equal 301, last_response.status
+ assert_equal "http://example.org/awesome/blog/foo", last_response.headers["Location"]
+ assert_equal %(<html><body>You are being <a href="http://example.org/awesome/blog/foo">redirected</a>.</body></html>), last_response.body
+ end
+
+ test "[ENGINE] relative custom redirect uses SCRIPT_NAME from request" do
+ get "/awesome/blog/relative_custom_redirect"
+ assert_equal 301, last_response.status
+ assert_equal "http://example.org/awesome/blog/foo", last_response.headers["Location"]
+ assert_equal %(<html><body>You are being <a href="http://example.org/awesome/blog/foo">redirected</a>.</body></html>), last_response.body
+ end
+
+ test "[ENGINE] absolute path redirect doesn't use SCRIPT_NAME from request" do
+ get "/awesome/blog/absolute_path_redirect"
+ assert_equal 301, last_response.status
+ assert_equal "http://example.org/foo", last_response.headers["Location"]
+ assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
+ end
+
+ test "[ENGINE] absolute option redirect doesn't use SCRIPT_NAME from request" do
+ get "/awesome/blog/absolute_option_redirect"
+ assert_equal 301, last_response.status
+ assert_equal "http://example.org/foo", last_response.headers["Location"]
+ assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
+ end
+
+ test "[ENGINE] absolute custom redirect doesn't use SCRIPT_NAME from request" do
+ get "/awesome/blog/absolute_custom_redirect"
+ assert_equal 301, last_response.status
+ assert_equal "http://example.org/foo", last_response.headers["Location"]
+ assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
+ end
+
# Inside Application
test "[APP] generating engine's route includes prefix" do
get "/generate"
@@ -281,6 +331,14 @@ module TestGenerationPrefix
routes = ActionDispatch::Routing::RouteSet.new
routes.draw do
get "/posts/:id", :to => "posts#show", :as => :post
+
+ get "/relative_path_redirect", :to => redirect("foo")
+ get "/relative_option_redirect", :to => redirect(:path => "foo")
+ get "/relative_custom_redirect", :to => redirect { |params, request| "foo" }
+
+ get "/absolute_path_redirect", :to => redirect("/foo")
+ get "/absolute_option_redirect", :to => redirect(:path => "/foo")
+ get "/absolute_custom_redirect", :to => redirect { |params, request| "/foo" }
end
routes
@@ -331,5 +389,47 @@ module TestGenerationPrefix
get "/posts/1"
assert_equal "/posts/1", last_response.body
end
+
+ test "[ENGINE] relative path redirect uses SCRIPT_NAME from request" do
+ get "/relative_path_redirect"
+ assert_equal 301, last_response.status
+ assert_equal "http://example.org/foo", last_response.headers["Location"]
+ assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
+ end
+
+ test "[ENGINE] relative option redirect uses SCRIPT_NAME from request" do
+ get "/relative_option_redirect"
+ assert_equal 301, last_response.status
+ assert_equal "http://example.org/foo", last_response.headers["Location"]
+ assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
+ end
+
+ test "[ENGINE] relative custom redirect uses SCRIPT_NAME from request" do
+ get "/relative_custom_redirect"
+ assert_equal 301, last_response.status
+ assert_equal "http://example.org/foo", last_response.headers["Location"]
+ assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
+ end
+
+ test "[ENGINE] absolute path redirect doesn't use SCRIPT_NAME from request" do
+ get "/absolute_path_redirect"
+ assert_equal 301, last_response.status
+ assert_equal "http://example.org/foo", last_response.headers["Location"]
+ assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
+ end
+
+ test "[ENGINE] absolute option redirect doesn't use SCRIPT_NAME from request" do
+ get "/absolute_option_redirect"
+ assert_equal 301, last_response.status
+ assert_equal "http://example.org/foo", last_response.headers["Location"]
+ assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
+ end
+
+ test "[ENGINE] absolute custom redirect doesn't use SCRIPT_NAME from request" do
+ get "/absolute_custom_redirect"
+ assert_equal 301, last_response.status
+ assert_equal "http://example.org/foo", last_response.headers["Location"]
+ assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
+ end
end
end
diff --git a/actionpack/test/fixtures/respond_with/respond_with_additional_params.html.erb b/actionpack/test/fixtures/respond_with/respond_with_additional_params.html.erb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/respond_with/respond_with_additional_params.html.erb
diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb
index 8e1aea50a9..d7e8e99200 100644
--- a/actionview/lib/action_view/helpers/date_helper.rb
+++ b/actionview/lib/action_view/helpers/date_helper.rb
@@ -531,7 +531,7 @@ module ActionView
# my_date = Time.now + 2.days
#
# # Generates a select field for days that defaults to the day for the date in my_date.
- # select_day(my_time)
+ # select_day(my_date)
#
# # Generates a select field for days that defaults to the number given.
# select_day(5)
@@ -541,7 +541,7 @@ module ActionView
#
# # Generates a select field for days that defaults to the day for the date in my_date
# # that is named 'due' rather than 'day'.
- # select_day(my_time, field_name: 'due')
+ # select_day(my_date, field_name: 'due')
#
# # Generates a select field for days with a custom prompt. Use <tt>prompt: true</tt> for a
# # generic prompt.
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index 8a4918a8c0..2f5246f42a 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -455,7 +455,7 @@ module ActionView
html_options, name = name, nil if block_given?
html_options = (html_options || {}).stringify_keys
- extras = %w{ cc bcc body subject }.map { |item|
+ extras = %w{ cc bcc body subject }.map! { |item|
option = html_options.delete(item) || next
"#{item}=#{Rack::Utils.escape_path(option)}"
}.compact
diff --git a/actionview/test/lib/controller/view_paths_test.rb b/actionview/test/actionpack/controller/view_paths_test.rb
index c6e7a523b9..c6e7a523b9 100644
--- a/actionview/test/lib/controller/view_paths_test.rb
+++ b/actionview/test/actionpack/controller/view_paths_test.rb
diff --git a/actionview/test/lib/controller/fake_controllers.rb b/actionview/test/lib/controller/fake_controllers.rb
deleted file mode 100644
index 1a2863b689..0000000000
--- a/actionview/test/lib/controller/fake_controllers.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-class ContentController < ActionController::Base; end
-
-module Admin
- class AccountsController < ActionController::Base; end
- class PostsController < ActionController::Base; end
- class StuffController < ActionController::Base; end
- class UserController < ActionController::Base; end
- class UsersController < ActionController::Base; end
-end
-
-module Api
- class UsersController < ActionController::Base; end
- class ProductsController < ActionController::Base; end
-end
-
-class AccountController < ActionController::Base; end
-class ArchiveController < ActionController::Base; end
-class ArticlesController < ActionController::Base; end
-class BarController < ActionController::Base; end
-class BlogController < ActionController::Base; end
-class BooksController < ActionController::Base; end
-class CarsController < ActionController::Base; end
-class CcController < ActionController::Base; end
-class CController < ActionController::Base; end
-class FooController < ActionController::Base; end
-class GeocodeController < ActionController::Base; end
-class NewsController < ActionController::Base; end
-class NotesController < ActionController::Base; end
-class PagesController < ActionController::Base; end
-class PeopleController < ActionController::Base; end
-class PostsController < ActionController::Base; end
-class SubpathBooksController < ActionController::Base; end
-class SymbolsController < ActionController::Base; end
-class UserController < ActionController::Base; end
-class UsersController < ActionController::Base; end
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index eb21b69163..e8602ecbcf 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Fix `has_secure_password` to honor bcrypt-ruby's cost attribute.
+
+ *T.J. Schuck*
+
* Updated the `ActiveModel::Dirty#changed_attributes` method to be indifferent between using
symbols and strings as keys.
diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec
index 51655fe3da..11e755649c 100644
--- a/activemodel/activemodel.gemspec
+++ b/activemodel/activemodel.gemspec
@@ -5,7 +5,7 @@ Gem::Specification.new do |s|
s.name = 'activemodel'
s.version = version
s.summary = 'A toolkit for building modeling frameworks (part of Rails).'
- s.description = 'A toolkit for building modeling frameworks like Active Record. Rich support for attributes, callbacks, validations, observers, serialization, internationalization, and testing.'
+ s.description = 'A toolkit for building modeling frameworks like Active Record. Rich support for attributes, callbacks, validations, serialization, internationalization, and testing.'
s.required_ruby_version = '>= 1.9.3'
diff --git a/activemodel/examples/validations.rb b/activemodel/examples/validations.rb
index c94cd17e18..b8e74acd5e 100644
--- a/activemodel/examples/validations.rb
+++ b/activemodel/examples/validations.rb
@@ -1,3 +1,4 @@
+require File.expand_path('../../../load_paths', __FILE__)
require 'active_model'
class Person
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index 17fafe4be9..f87c36e39e 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -103,7 +103,7 @@ module ActiveModel
def password=(unencrypted_password)
unless unencrypted_password.blank?
@password = unencrypted_password
- cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine::DEFAULT_COST
+ cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost)
end
end
diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb
index 98e5c747d5..41d0b2263e 100644
--- a/activemodel/test/cases/secure_password_test.rb
+++ b/activemodel/test/cases/secure_password_test.rb
@@ -82,6 +82,14 @@ class SecurePasswordTest < ActiveModel::TestCase
assert_equal BCrypt::Engine::DEFAULT_COST, @user.password_digest.cost
end
+ test "Password digest cost honors bcrypt cost attribute when min_cost is false" do
+ ActiveModel::SecurePassword.min_cost = false
+ BCrypt::Engine.cost = 5
+
+ @user.password = "secret"
+ assert_equal BCrypt::Engine.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
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 83c94caa53..658645275d 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,9 @@
+* For missed association exception message
+ which is raised in `ActiveRecord::Associations::Preloader` class
+ added owner record class name in order to simplify to find problem code.
+
+ *Paul Nikitochkin*
+
* `has_and_belongs_to_many` is now transparently implemented in terms of
`has_many :through`. Behavior should remain the same, if not, it is a bug.
@@ -199,6 +205,12 @@
*Yves Senn*
+* Fixes bug when using includes combined with select, the select statement was overwritten.
+
+ Fixes #11773
+
+ *Edo Balvers*
+
* Load fixtures from linked folders.
*Kassio Borges*
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index 4066a1c9d3..d8d68eb908 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -18,18 +18,15 @@ module ActiveRecord::Associations::Builder
VALID_OPTIONS = [:class_name, :class, :foreign_key, :validate]
- attr_reader :name, :scope, :options
-
def self.build(model, name, scope, options, &block)
- builder = create_builder model, name, scope, options, &block
- reflection = builder.build(model)
- builder.define_accessors model, reflection
+ extension = define_extensions model, name, &block
+ reflection = create_reflection model, name, scope, options, extension
+ define_accessors model, reflection
define_callbacks model, reflection
- builder.define_extensions model
reflection
end
- def self.create_builder(model, name, scope, options, &block)
+ def self.create_reflection(model, name, scope, options, extension = nil)
raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
if scope.is_a?(Hash)
@@ -37,38 +34,44 @@ module ActiveRecord::Associations::Builder
scope = nil
end
- new(name, scope, options, &block)
- end
+ validate_options(options)
+
+ scope = build_scope(scope, extension)
- def initialize(name, scope, options)
- @name = name
- @scope = scope
- @options = options
+ ActiveRecord::Reflection.create(macro, name, scope, options, model)
+ end
- validate_options
+ def self.build_scope(scope, extension)
+ new_scope = scope
if scope && scope.arity == 0
- @scope = proc { instance_exec(&scope) }
+ new_scope = proc { instance_exec(&scope) }
+ end
+
+ if extension
+ new_scope = wrap_scope new_scope, extension
end
+
+ new_scope
end
- def build(model)
- ActiveRecord::Reflection.create(macro, name, scope, options, model)
+ def self.wrap_scope(scope, extension)
+ scope
end
- def macro
+ def self.macro
raise NotImplementedError
end
- def valid_options
+ def self.valid_options(options)
VALID_OPTIONS + Association.extensions.flat_map(&:valid_options)
end
- def validate_options
- options.assert_valid_keys(valid_options)
+ def self.validate_options(options)
+ options.assert_valid_keys(valid_options(options))
end
- def define_extensions(model)
+ def self.define_extensions(model, name)
end
def self.define_callbacks(model, reflection)
@@ -84,14 +87,14 @@ module ActiveRecord::Associations::Builder
# end
#
# Post.first.comments and Post.first.comments= methods are defined by this method...
-
- def define_accessors(model, reflection)
+ def self.define_accessors(model, reflection)
mixin = model.generated_feature_methods
- define_readers(mixin)
- define_writers(mixin)
+ name = reflection.name
+ define_readers(mixin, name)
+ define_writers(mixin, name)
end
- def define_readers(mixin)
+ def self.define_readers(mixin, name)
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
def #{name}(*args)
association(:#{name}).reader(*args)
@@ -99,7 +102,7 @@ module ActiveRecord::Associations::Builder
CODE
end
- def define_writers(mixin)
+ def self.define_writers(mixin, name)
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
def #{name}=(value)
association(:#{name}).writer(value)
@@ -111,8 +114,6 @@ module ActiveRecord::Associations::Builder
raise NotImplementedError
end
- private
-
def self.add_before_destroy_callbacks(model, reflection)
unless valid_dependent_options.include? reflection.options[:dependent]
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{reflection.options[:dependent]}"
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index 9d55ec850e..aa43c34d86 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -1,10 +1,10 @@
module ActiveRecord::Associations::Builder
class BelongsTo < SingularAssociation #:nodoc:
- def macro
+ def self.macro
:belongs_to
end
- def valid_options
+ def self.valid_options(options)
super + [:foreign_type, :polymorphic, :touch]
end
@@ -18,14 +18,12 @@ module ActiveRecord::Associations::Builder
add_touch_callbacks(model, reflection) if reflection.options[:touch]
end
- def define_accessors(mixin, reflection)
+ def self.define_accessors(mixin, reflection)
super
add_counter_cache_methods mixin
end
- private
-
- def add_counter_cache_methods(mixin)
+ def self.add_counter_cache_methods(mixin)
return if mixin.method_defined? :belongs_to_counter_cache_after_create
mixin.class_eval do
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index 15f9f9a65f..2ff67f904d 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -1,4 +1,4 @@
-# This class is inherited by the has_many and has_many_and_belongs_to_many association classes
+# This class is inherited by the has_many and has_many_and_belongs_to_many association classes
require 'active_record/associations'
@@ -7,22 +7,11 @@ module ActiveRecord::Associations::Builder
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
- def valid_options
+ def self.valid_options(options)
super + [:table_name, :before_add,
:after_add, :before_remove, :after_remove, :extend]
end
- attr_reader :block_extension
-
- def initialize(name, scope, options)
- super
- @mod = nil
- if block_given?
- @mod = Module.new(&Proc.new)
- @scope = wrap_scope @scope, @mod
- end
- end
-
def self.define_callbacks(model, reflection)
super
name = reflection.name
@@ -32,10 +21,11 @@ module ActiveRecord::Associations::Builder
}
end
- def define_extensions(model)
- if @mod
+ def self.define_extensions(model, name)
+ if block_given?
extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
- model.parent.const_set(extension_module_name, @mod)
+ extension = Module.new(&Proc.new)
+ model.parent.const_set(extension_module_name, extension)
end
end
@@ -58,8 +48,7 @@ module ActiveRecord::Associations::Builder
end
# Defines the setter and getter methods for the collection_singular_ids.
-
- def define_readers(mixin)
+ def self.define_readers(mixin, name)
super
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
@@ -69,7 +58,7 @@ module ActiveRecord::Associations::Builder
CODE
end
- def define_writers(mixin)
+ def self.define_writers(mixin, name)
super
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
@@ -79,9 +68,7 @@ module ActiveRecord::Associations::Builder
CODE
end
- private
-
- def wrap_scope(scope, mod)
+ def self.wrap_scope(scope, mod)
if scope
proc { |owner| instance_exec(owner, &scope).extending(mod) }
else
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index af596a3a64..1c9c04b044 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -84,11 +84,11 @@ module ActiveRecord::Associations::Builder
middle_name = [lhs_model.name.downcase.pluralize,
association_name].join('_').gsub(/::/, '_').to_sym
middle_options = middle_options join_model
- hm_builder = HasMany.create_builder(lhs_model,
- middle_name,
- nil,
- middle_options)
- hm_builder.build lhs_model
+
+ HasMany.create_reflection(lhs_model,
+ middle_name,
+ nil,
+ middle_options)
end
private
diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb
index 7909b93622..227184cd19 100644
--- a/activerecord/lib/active_record/associations/builder/has_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_many.rb
@@ -1,10 +1,10 @@
module ActiveRecord::Associations::Builder
class HasMany < CollectionAssociation #:nodoc:
- def macro
+ def self.macro
:has_many
end
- def valid_options
+ def self.valid_options(options)
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache]
end
diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb
index f359efd496..064a3c8b51 100644
--- a/activerecord/lib/active_record/associations/builder/has_one.rb
+++ b/activerecord/lib/active_record/associations/builder/has_one.rb
@@ -1,10 +1,10 @@
module ActiveRecord::Associations::Builder
class HasOne < SingularAssociation #:nodoc:
- def macro
+ def self.macro
:has_one
end
- def valid_options
+ def self.valid_options(options)
valid = super + [:order, :as]
valid += [:through, :source, :source_type] if options[:through]
valid
@@ -14,8 +14,6 @@ module ActiveRecord::Associations::Builder
[:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
end
- private
-
def self.add_before_destroy_callbacks(model, reflection)
super unless reflection.options[:through]
end
diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb
index 10d568ebc0..2a4b1c441f 100644
--- a/activerecord/lib/active_record/associations/builder/singular_association.rb
+++ b/activerecord/lib/active_record/associations/builder/singular_association.rb
@@ -1,19 +1,18 @@
-# This class is inherited by the has_one and belongs_to association classes
+# This class is inherited by the has_one and belongs_to association classes
module ActiveRecord::Associations::Builder
class SingularAssociation < Association #:nodoc:
- def valid_options
+ def self.valid_options(options)
super + [:remote, :dependent, :counter_cache, :primary_key, :inverse_of]
end
- def define_accessors(model, reflection)
+ def self.define_accessors(model, reflection)
super
- define_constructors(model.generated_feature_methods) if reflection.constructable?
+ define_constructors(model.generated_feature_methods, reflection.name) if reflection.constructable?
end
# Defines the (build|create)_association methods for belongs_to or has_one association
-
- def define_constructors(mixin)
+ def self.define_constructors(mixin, name)
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
def build_#{name}(*args, &block)
association(:#{name}).build(*args, &block)
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index 3acb50d73f..6e08f67286 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -4,7 +4,31 @@ module ActiveRecord
autoload :JoinBase, 'active_record/associations/join_dependency/join_base'
autoload :JoinAssociation, 'active_record/associations/join_dependency/join_association'
- attr_reader :join_parts, :reflections, :alias_tracker, :base_klass
+ attr_reader :alias_tracker, :base_klass, :join_root
+
+ def self.make_tree(associations)
+ hash = {}
+ walk_tree associations, hash
+ hash
+ end
+
+ def self.walk_tree(associations, hash)
+ case associations
+ when Symbol, String
+ hash[associations.to_sym] ||= {}
+ when Array
+ associations.each do |assoc|
+ walk_tree assoc, hash
+ end
+ when Hash
+ associations.each do |k,v|
+ cache = hash[k] ||= {}
+ walk_tree v, cache
+ end
+ else
+ raise ConfigurationError, associations.inspect
+ end
+ end
# base is the base class on which operation is taking place.
# associations is the list of associations which are joined using hash, symbol or array.
@@ -30,38 +54,37 @@ module ActiveRecord
def initialize(base, associations, joins)
@base_klass = base
@table_joins = joins
- @join_parts = [JoinBase.new(base)]
- @associations = {}
- @reflections = []
+ @join_root = JoinBase.new(base)
@alias_tracker = AliasTracker.new(base.connection, joins)
@alias_tracker.aliased_name_for(base.table_name) # Updates the count for base.table_name to 1
- build(associations, join_parts.last, Arel::InnerJoin)
+ tree = self.class.make_tree associations
+ build tree, @join_root, Arel::InnerJoin
end
- def graft(*associations)
- associations.each do |association|
- join_associations.detect {|a| association == a} ||
- build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type)
- end
- self
+ def reflections
+ join_root.drop(1).map!(&:reflection)
end
- def join_associations
- join_parts.drop 1
- end
+ def merge_outer_joins!(other)
+ left = join_root
+ right = other.join_root
- def join_base
- join_parts.first
+ if left.match? right
+ merge_node left, right
+ else
+ # If the roots aren't the same, then deep copy the RHS to the LHS
+ left.children.concat right.children.map { |node|
+ deep_copy left, node
+ }
+ end
end
- def join_relation(relation)
- join_associations.inject(relation) do |rel,association|
- association.join_relation(rel)
- end
+ def join_constraints
+ join_root.children.flat_map { |c| c.flat_map(&:join_constraints) }
end
def columns
- join_parts.collect { |join_part|
+ join_root.collect { |join_part|
table = join_part.aliased_table
join_part.column_names_with_alias.collect{ |column_name, aliased_name|
table[column_name].as Arel.sql(aliased_name)
@@ -69,100 +92,80 @@ module ActiveRecord
}.flatten
end
- def instantiate(rows)
- primary_key = join_base.aliased_primary_key
+ def instantiate(result_set)
+ primary_key = join_root.aliased_primary_key
parents = {}
- records = rows.map { |model|
- primary_id = model[primary_key]
- parent = parents[primary_id] ||= join_base.instantiate(model)
- construct(parent, @associations, join_associations, model)
+ type_caster = result_set.column_type primary_key
+
+ records = result_set.map { |row_hash|
+ primary_id = type_caster.type_cast row_hash[primary_key]
+ parent = parents[primary_id] ||= join_root.instantiate(row_hash)
+ construct(parent, join_root, row_hash, result_set)
parent
}.uniq
- remove_duplicate_results!(base_klass, records, @associations)
+ remove_duplicate_results!(base_klass, records, join_root.children)
records
end
- protected
+ private
+
+ def merge_node(left, right)
+ intersection, missing = right.children.map { |node1|
+ [left.children.find { |node2| node1.match? node2 }, node1]
+ }.partition(&:first)
+
+ intersection.each { |l,r| merge_node l, r }
+
+ left.children.concat missing.map { |_,node| deep_copy left, node }
+ end
+
+ def deep_copy(parent, node)
+ dup = build_join_association(node.reflection, parent, Arel::OuterJoin)
+ dup.children.concat node.children.map { |n| deep_copy dup, n }
+ dup
+ end
def remove_duplicate_results!(base, records, associations)
- case associations
- when Symbol, String
- reflection = base.reflections[associations]
+ associations.each do |node|
+ reflection = base.reflect_on_association(node.name)
remove_uniq_by_reflection(reflection, records)
- when Array
- associations.each do |association|
- remove_duplicate_results!(base, records, association)
- end
- when Hash
- associations.each_key do |name|
- reflection = base.reflections[name]
- remove_uniq_by_reflection(reflection, records)
-
- parent_records = []
- records.each do |record|
- if descendant = record.send(reflection.name)
- if reflection.collection?
- parent_records.concat descendant.target.uniq
- else
- parent_records << descendant
- end
+
+ parent_records = []
+ records.each do |record|
+ if descendant = record.send(reflection.name)
+ if reflection.collection?
+ parent_records.concat descendant.target.uniq
+ else
+ parent_records << descendant
end
end
+ end
- remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty?
+ unless parent_records.empty?
+ remove_duplicate_results!(reflection.klass, parent_records, node.children)
end
end
end
- def cache_joined_association(association)
- associations = []
- parent = association.parent
- while parent != join_base
- associations.unshift(parent.reflection.name)
- parent = parent.parent
- end
- ref = associations.inject(@associations) do |cache,key|
- cache[key]
- end
- ref[association.reflection.name] ||= {}
+ def find_reflection(klass, name)
+ klass.reflect_on_association(name) or
+ raise ConfigurationError, "Association named '#{ name }' was not found on #{ klass.name }; perhaps you misspelled it?"
end
def build(associations, parent, join_type)
- case associations
- when Symbol, String
- reflection = parent.reflections[associations.intern] or
- raise ConfigurationError, "Association named '#{ associations }' was not found on #{ parent.base_klass.name }; perhaps you misspelled it?"
- unless join_association = find_join_association(reflection, parent)
- @reflections << reflection
- join_association = build_join_association(reflection, parent, join_type)
- @join_parts << join_association
- cache_joined_association(join_association)
- end
- join_association
- when Array
- associations.each do |association|
- build(association, parent, join_type)
- end
- when Hash
- associations.each do |left, right|
- join_association = build(left, parent, join_type)
- build(right, join_association, join_type)
- end
- else
- raise ConfigurationError, associations.inspect
+ associations.each do |name, right|
+ reflection = find_reflection parent.base_klass, name
+ join_association = build_join_association reflection, parent, join_type
+ parent.children << join_association
+ build right, join_association, join_type
end
end
- def find_join_association(name_or_reflection, parent)
- if String === name_or_reflection
- name_or_reflection = name_or_reflection.to_sym
- end
-
- join_associations.detect { |j|
- j.reflection == name_or_reflection && j.parent == parent
- }
+ def build_scalar(reflection, parent, join_type)
+ join_association = build_join_association(reflection, parent, join_type)
+ parent.children << join_association
end
def remove_uniq_by_reflection(reflection, records)
@@ -178,38 +181,21 @@ module ActiveRecord
raise EagerLoadPolymorphicError.new(reflection)
end
- JoinAssociation.new(reflection, self, parent, join_type)
+ JoinAssociation.new(reflection, join_root.to_a.length, parent, join_type, alias_tracker)
end
- def construct(parent, associations, join_parts, row)
- case associations
- when Symbol, String
- name = associations.to_s
-
- join_part = join_parts.detect { |j|
- j.reflection.name.to_s == name &&
- j.parent_table_name == parent.class.table_name }
-
- raise(ConfigurationError, "No such association") unless join_part
-
- join_parts.delete(join_part)
- construct_association(parent, join_part, row)
- when Array
- associations.each do |association|
- construct(parent, association, join_parts, row)
- end
- when Hash
- associations.sort_by { |k,_| k.to_s }.each do |association_name, assoc|
- association = construct(parent, association_name, join_parts, row)
- construct(association, assoc, join_parts, row) if association
- end
- else
- raise ConfigurationError, associations.inspect
+ def construct(ar_parent, parent, row, rs)
+ parent.children.each do |node|
+ association = construct_association(ar_parent, parent, node, row, rs)
+ construct(association, node, row, rs) if association
end
end
- def construct_association(record, join_part, row)
- return if record.id.to_s != join_part.parent.record_id(row).to_s
+ def construct_association(record, parent, join_part, row, rs)
+ caster = rs.column_type(parent.aliased_primary_key)
+ row_id = caster.type_cast row[parent.aliased_primary_key]
+
+ return if record.id != row_id
macro = join_part.reflection.macro
if macro == :has_one
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
index 2307564ce7..3af613d2d1 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -9,15 +9,6 @@ module ActiveRecord
# The reflection of the association represented
attr_reader :reflection
- # The JoinDependency object which this JoinAssociation exists within. This is mainly
- # relevant for generating aliases which do not conflict with other joins which are
- # part of the query.
- attr_reader :join_dependency
-
- # A JoinBase instance representing the active record we are joining onto.
- # (So in Author.has_many :posts, the Author would be that base record.)
- attr_reader :parent
-
# What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin
attr_accessor :join_type
@@ -25,39 +16,26 @@ module ActiveRecord
attr_reader :aliased_prefix
attr_reader :tables
+ attr_reader :alias_tracker
delegate :options, :through_reflection, :source_reflection, :chain, :to => :reflection
- delegate :alias_tracker, :to => :join_dependency
- def initialize(reflection, join_dependency, parent, join_type)
- super(reflection.klass)
+ def initialize(reflection, index, parent, join_type, alias_tracker)
+ super(reflection.klass, parent)
@reflection = reflection
- @join_dependency = join_dependency
- @parent = parent
+ @alias_tracker = alias_tracker
@join_type = join_type
- @aliased_prefix = "t#{ join_dependency.join_parts.size }"
+ @aliased_prefix = "t#{ index }"
@tables = construct_tables.reverse
end
def parent_table_name; parent.table_name; end
alias :alias_suffix :parent_table_name
- def ==(other)
- other.class == self.class &&
- other.reflection == reflection &&
- other.parent == parent
- end
-
- def find_parent_in(other_join_dependency)
- other_join_dependency.join_parts.detect do |join_part|
- case parent
- when JoinBase
- parent.base_klass == join_part.base_klass
- else
- parent == join_part
- end
- end
+ def match?(other)
+ return true if self == other
+ super && reflection == other.reflection
end
def join_constraints
@@ -147,11 +125,6 @@ module ActiveRecord
constraint
end
- def join_relation(joining_relation)
- self.join_type = Arel::OuterJoin
- joining_relation.joins(self)
- end
-
def table
tables.last
end
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_base.rb b/activerecord/lib/active_record/associations/join_dependency/join_base.rb
index c689d06594..48de12bcd5 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_base.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_base.rb
@@ -4,9 +4,13 @@ module ActiveRecord
module Associations
class JoinDependency # :nodoc:
class JoinBase < JoinPart # :nodoc:
- def ==(other)
- other.class == self.class &&
- other.base_klass == base_klass
+ def initialize(klass)
+ super(klass, nil)
+ end
+
+ def match?(other)
+ return true if self == other
+ super && base_klass == other.base_klass
end
def aliased_prefix
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/join_dependency/join_part.rb
index 8024105472..d39ce94c99 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_part.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_part.rb
@@ -8,25 +8,42 @@ module ActiveRecord
# operations (for example a has_and_belongs_to_many JoinAssociation would result in
# two; one for the join table and one for the target table).
class JoinPart # :nodoc:
+ include Enumerable
+
+ # A JoinBase instance representing the active record we are joining onto.
+ # (So in Author.has_many :posts, the Author would be that base record.)
+ attr_reader :parent
+
# The Active Record class which this join part is associated 'about'; for a JoinBase
# this is the actual base model, for a JoinAssociation this is the target model of the
# association.
- attr_reader :base_klass
+ attr_reader :base_klass, :children
- delegate :table_name, :column_names, :primary_key, :reflections, :arel_engine, :to => :base_klass
+ delegate :table_name, :column_names, :primary_key, :arel_engine, :to => :base_klass
- def initialize(base_klass)
+ def initialize(base_klass, parent)
@base_klass = base_klass
+ @parent = parent
@cached_record = {}
@column_names_with_alias = nil
+ @children = []
end
- def aliased_table
- Arel::Nodes::TableAlias.new table, aliased_table_name
+ def name
+ reflection.name
end
- def ==(other)
- raise NotImplementedError
+ def match?(other)
+ self.class == other.class
+ end
+
+ def each(&block)
+ yield self
+ children.each { |child| child.each(&block) }
+ end
+
+ def aliased_table
+ Arel::Nodes::TableAlias.new table, aliased_table_name
end
# An Arel::Table for the active_record
@@ -54,7 +71,7 @@ module ActiveRecord
unless @column_names_with_alias
@column_names_with_alias = []
- ([primary_key] + (column_names - [primary_key])).compact.each_with_index do |column_name, i|
+ column_names.each_with_index do |column_name, i|
@column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"]
end
end
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 713ff80d47..2393667ac8 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -153,13 +153,13 @@ module ActiveRecord
records.group_by do |record|
reflection = record.class.reflect_on_association(association)
- reflection || raise_config_error(association)
+ reflection || raise_config_error(record, association)
end
end
- def raise_config_error(association)
+ def raise_config_error(record, association)
raise ActiveRecord::ConfigurationError,
- "Association named '#{association}' was not found; " \
+ "Association named '#{association}' was not found on #{record.class.name}; " \
"perhaps you misspelled it?"
end
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index ea21836c65..3166df57eb 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -15,7 +15,7 @@ module ActiveRecord
through_reflection.name,
through_scope)
- through_records = owners.map do |owner, h|
+ through_records = owners.map do |owner|
association = owner.association through_reflection.name
[owner, Array(association.reader)]
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index e4c484d64b..128a9377c1 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -23,11 +23,14 @@ module ActiveRecord
# Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
# <tt>after_rollback</tt>.
#
+ # Additionally, an <tt>after_touch</tt> callback is triggered whenever an
+ # object is touched.
+ #
# Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
# is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
# are instantiated as well.
#
- # That's a total of twelve callbacks, which gives you immense power to react and prepare for each state in the
+ # There are nineteen callbacks in total, which give you immense power to react and prepare for each state in the
# Active Record life cycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar,
# except that each <tt>_create</tt> callback is replaced by the corresponding <tt>_update</tt> callback.
#
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index e826762def..7e1e120288 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -160,7 +160,7 @@ module ActiveRecord
end
def type_condition(table = arel_table)
- sti_column = table[inheritance_column.to_sym]
+ sti_column = table[inheritance_column]
sti_names = ([self] + descendants).map { |model| model.sti_name }
sti_column.in(sti_names)
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 267aa28058..a73a140ef1 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -401,7 +401,8 @@ module ActiveRecord
end
# Saves the record with the updated_at/on attributes set to the current time.
- # Please note that no validation is performed and no callbacks are executed.
+ # Please note that no validation is performed and only the +after_touch+
+ # callback is executed.
# If an attribute name is passed, that attribute is updated along with
# updated_at/on attributes.
#
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 8ddf5cfa66..e88c5d17cb 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -367,21 +367,22 @@ module ActiveRecord
protected
- def actual_source_reflection # FIXME: this is a horrible name
- self
- end
+ def actual_source_reflection # FIXME: this is a horrible name
+ self
+ end
private
- def calculate_constructable(macro, options)
- case macro
- when :belongs_to
- !options[:polymorphic]
- when :has_one
- !options[:through]
- else
- true
+
+ def calculate_constructable(macro, options)
+ case macro
+ when :belongs_to
+ !options[:polymorphic]
+ when :has_one
+ !options[:through]
+ else
+ true
+ end
end
- end
# Attempts to find the inverse association name automatically.
# If it cannot find a suitable inverse association name, it returns
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 0132a02f83..8583286de5 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -261,13 +261,13 @@ module ActiveRecord
end
def construct_relation_for_association_find(join_dependency)
- relation = except(:select).select(join_dependency.columns)
+ relation = except(:select).select(join_dependency.columns + select_values)
apply_join_dependency(relation, join_dependency)
end
def apply_join_dependency(relation, join_dependency)
relation = relation.except(:includes, :eager_load, :preload)
- relation = join_dependency.join_relation(relation)
+ relation = relation.joins join_dependency
if using_limitable_reflections?(join_dependency.reflections)
relation
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index c05632e688..182b9ed89c 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -94,7 +94,7 @@ module ActiveRecord
[])
relation.joins! rest
- @relation = join_dependency.join_relation(relation)
+ @relation = relation.joins join_dependency
end
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 9fcb6db726..9c9690215a 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -928,7 +928,7 @@ module ActiveRecord
:string_join
when Hash, Symbol, Array
:association_join
- when ActiveRecord::Associations::JoinDependency::JoinAssociation
+ when ActiveRecord::Associations::JoinDependency
:stashed_join
when Arel::Nodes::Join
:join_node
@@ -950,10 +950,11 @@ module ActiveRecord
join_list
)
- join_dependency.graft(*stashed_association_joins)
+ stashed_association_joins.each do |dep|
+ join_dependency.merge_outer_joins! dep
+ end
- joins = join_dependency.join_associations.map!(&:join_constraints)
- joins.flatten!
+ joins = join_dependency.join_constraints
joins.each { |join| manager.from(join) }
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index d0f1cb5b75..1dc3bf3f12 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -46,6 +46,10 @@ module ActiveRecord
IDENTITY_TYPE
end
+ def column_type(name)
+ @column_types[name] || identity_type
+ end
+
def each
if block_given?
hash_rows.each { |row| yield row }
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index 47dff7d0ea..f8f2832ab1 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -75,7 +75,6 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
private
def extend!(model)
- builder = ActiveRecord::Associations::Builder::HasMany.new(:association_name, nil, {}) { }
- builder.define_extensions(model)
+ ActiveRecord::Associations::Builder::HasMany.define_extensions(model, :association_name) { }
end
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 5299e4e17e..3b61b91d62 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -785,7 +785,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
sarah = Person.create!(:first_name => 'Sarah', :primary_contact_id => people(:susan).id, :gender => 'F', :number1_fan_id => 1)
john = Person.create!(:first_name => 'John', :primary_contact_id => sarah.id, :gender => 'M', :number1_fan_id => 1)
assert_equal sarah.agents, [john]
- assert_equal people(:susan).agents.map(&:agents).flatten, people(:susan).agents_of_agents
+ assert_equal people(:susan).agents.flat_map(&:agents), people(:susan).agents_of_agents
end
def test_associate_existing_with_nonstandard_primary_key_on_belongs_to
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index f814947ab2..396d4ef1e9 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -486,6 +486,14 @@ class RelationTest < ActiveRecord::TestCase
assert_equal Developer.where(name: 'David').map(&:id).sort, developers
end
+ def test_includes_with_select
+ query = Post.select('comments_count AS ranking').order('ranking').includes(:comments)
+ .where(comments: { id: 1 })
+
+ assert_equal ['comments_count AS ranking'], query.select_values
+ assert_equal 1, query.to_a.size
+ end
+
def test_loading_with_one_association
posts = Post.preload(:comments)
post = posts.find { |p| p.id == 1 }
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index 93a2b89ede..61fd762304 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -105,7 +105,7 @@ will be the template used for the email, formatted in HTML:
<h1>Welcome to example.com, <%= @user.name %></h1>
<p>
You have successfully signed up to example.com,
- your username is: <%= @user.login %>.<br/>
+ your username is: <%= @user.login %>.<br>
</p>
<p>
To login to the site, just follow this link: <%= @url %>.
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index 5cda104138..d19dd11181 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -68,7 +68,7 @@ Consider the following loop for names:
```html+erb
<h1>Names of all the people</h1>
<% @people.each do |person| %>
- Name: <%= person.name %><br/>
+ Name: <%= person.name %><br>
<% end %>
```
@@ -269,7 +269,7 @@ Rails will render the `_product_ruler` partial (with no data passed to it) betwe
### Layouts
-Layouts can be used to render a common view template around the results of Rails controller actions. Typically, every Rails has a couple of overall layouts that most pages are rendered within. For example, a site might have a layout for a logged in user, and a layout for the marketing or sales side of the site. The logged in user layout might include top-level navigation that should be present across many controller actions. The sales layout for a SaaS app might include top-level navigation for things like "Pricing" and "Contact Us." You would expect each layout to have a different look and feel. You can read more details about Layouts in the [Layouts and Rendering in Rails](layouts_and_rendering.html) guide.
+Layouts can be used to render a common view template around the results of Rails controller actions. Typically, every Rails application has a couple of overall layouts that most pages are rendered within. For example, a site might have a layout for a logged in user, and a layout for the marketing or sales side of the site. The logged in user layout might include top-level navigation that should be present across many controller actions. The sales layout for a SaaS app might include top-level navigation for things like "Pricing" and "Contact Us." You would expect each layout to have a different look and feel. You can read more details about Layouts in the [Layouts and Rendering in Rails](layouts_and_rendering.html) guide.
Partial Layouts
---------------
diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md
index 34baae509b..a184f0753d 100644
--- a/guides/source/active_record_basics.md
+++ b/guides/source/active_record_basics.md
@@ -116,7 +116,7 @@ to Active Record instances:
locking](http://api.rubyonrails.org/classes/ActiveRecord/Locking.html) to
a model.
* `type` - Specifies that the model uses [Single Table
- Inheritance](http://api.rubyonrails.org/classes/ActiveRecord/Base.html).
+ Inheritance](http://api.rubyonrails.org/classes/ActiveRecord/Base.html#label-Single+table+inheritance).
* `(association_name)_type` - Stores the type for
[polymorphic associations](association_basics.html#polymorphic-associations).
* `(table_name)_count` - Used to cache the number of belonging objects on
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index 0b2f0a47fa..797b996357 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -528,7 +528,7 @@ If you validate the presence of an object associated via a `has_one` or
Since `false.blank?` is true, if you want to validate the presence of a boolean
field you should use `validates :field_name, inclusion: { in: [true, false] }`.
-The default error message is _"can't be empty"_.
+The default error message is _"can't be blank"_.
### `absence`
@@ -783,7 +783,7 @@ end
Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank
```
-There is also an ability to pass custom exception to `:strict` option
+There is also an ability to pass custom exception to `:strict` option.
```ruby
class Person < ActiveRecord::Base
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 51ff921d7b..1b0b93c3bc 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -379,12 +379,13 @@ About your application's environment
Ruby version 1.9.3 (x86_64-linux)
RubyGems version 1.3.6
Rack version 1.3
-Rails version 4.0.0
+Rails version 4.1.0
JavaScript Runtime Node.js (V8)
-Active Record version 4.0.0
-Action Pack version 4.0.0
-Action Mailer version 4.0.0
-Active Support version 4.0.0
+Active Record version 4.1.0
+Action Pack version 4.1.0
+Action View version 4.1.0
+Action Mailer version 4.1.0
+Active Support version 4.1.0
Middleware Rack::Sendfile, ActionDispatch::Static, Rack::Lock, #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007ffd131a7c88>, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, Rack::Head, Rack::ConditionalGet, Rack::ETag
Application root /home/foobar/commandsapp
Environment development
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index bb2e8e906f..2f322d15da 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -1490,8 +1490,8 @@ So first, we'll wire up the Post show template
</p>
<% end %>
-<%= link_to 'Edit Post', edit_post_path(@post) %> |
-<%= link_to 'Back to Posts', posts_path %>
+<%= link_to 'Back', posts_path %>
+| <%= link_to 'Edit', edit_post_path(@post) %>
```
This adds a form on the `Post` show page that creates a new comment by
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index 91d12b4432..fe6b1ad906 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -468,14 +468,24 @@ def initialize!(group=:default) #:nodoc:
end
```
-As you can see, you can only initialize an app once. This is also where the initializers are run.
+As you can see, you can only initialize an app once. The initializers are run through
+the `run_initializers` method which is defined in `railties/lib/rails/initializable.rb`
-TODO: review this
+```ruby
+def run_initializers(group=:default, *args)
+ return if instance_variable_defined?(:@ran)
+ initializers.tsort_each do |initializer|
+ initializer.run(*args) if initializer.belongs_to?(group)
+ end
+ @ran = true
+end
+```
-The initializers code itself is tricky. What Rails is doing here is it
-traverses all the class ancestors looking for an `initializers` method,
-sorting them and running them. For example, the `Engine` class will make
-all the engines available by providing the `initializers` method.
+The run_initializers code itself is tricky. What Rails is doing here is
+traversing all the class ancestors looking for those that respond to an
+`initializers` method. It then sorts the ancestors by name, and runs them.
+For example, the `Engine` class will make all the engines available by
+providing an `initializers` method on them.
The `Rails::Application` class, as defined in `railties/lib/rails/application.rb`
defines `bootstrap`, `railtie`, and `finisher` initializers. The `bootstrap` initializers
diff --git a/guides/source/plugins.md b/guides/source/plugins.md
index ca55ee0df2..d0aa2e55a2 100644
--- a/guides/source/plugins.md
+++ b/guides/source/plugins.md
@@ -34,9 +34,15 @@ different rails applications using RubyGems and Bundler if desired.
Rails ships with a `rails plugin new` command which creates a
- skeleton for developing any kind of Rails extension with the ability
- to run integration tests using a dummy Rails application. See usage
- and options by asking for help:
+skeleton for developing any kind of Rails extension with the ability
+to run integration tests using a dummy Rails application. Create your
+plugin with the command:
+
+```bash
+$ rails plugin new yaffle
+```
+
+See usage and options by asking for help:
```bash
$ rails plugin --help
diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md
index 642c70fd9d..d53e0cd2bd 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -225,9 +225,13 @@ config.middleware.delete "Rack::MethodOverride"
Much of Action Controller's functionality is implemented as Middlewares. The following list explains the purpose of each of them:
+ **`Rack::Sendfile`**
+
+* Sets server specific X-Sendfile header. Configure this via `config.action_dispatch.x_sendfile_header` option.
+
**`ActionDispatch::Static`**
-* Used to serve static assets. Disabled if `config.serve_static_assets` is true.
+* Used to serve static assets. Disabled if `config.serve_static_assets` is `false`.
**`Rack::Lock`**
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index a4babbe8c3..4e6bf899af 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Expose MiddlewareStack#unshift to environment configuration.
+
+ *Ben Pickles*
+
* Include `web-console` into newly generated applications' Gemfile.
*Genadi Samokovarov*
diff --git a/railties/lib/rails/cli.rb b/railties/lib/rails/cli.rb
index 20313a2608..dd70c272c6 100644
--- a/railties/lib/rails/cli.rb
+++ b/railties/lib/rails/cli.rb
@@ -1,4 +1,3 @@
-require 'rbconfig'
require 'rails/app_rails_loader'
# If we are inside a Rails application this method performs an exec and thus
diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb
index 485bd1eb09..4201dac42f 100644
--- a/railties/lib/rails/commands/server.rb
+++ b/railties/lib/rails/commands/server.rb
@@ -1,6 +1,7 @@
require 'fileutils'
require 'optparse'
require 'action_dispatch'
+require 'rails'
module Rails
class Server < ::Rack::Server
@@ -32,7 +33,7 @@ module Rails
opt_parser.parse! args
- options[:log_stdout] = options[:daemonize].blank? && options[:environment] == "development"
+ options[:log_stdout] = options[:daemonize].blank? && (options[:environment] || Rails.env) == "development"
options[:server] = args.shift
options
end
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index c694513960..f5d7dede66 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -59,6 +59,10 @@ module Rails
@operations << [__method__, args, block]
end
+ def unshift(*args, &block)
+ @operations << [__method__, args, block]
+ end
+
def merge_into(other) #:nodoc:
@operations.each do |operation, args, block|
other.send(operation, *args, &block)
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 31a35a09bb..20d1d76d78 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -144,6 +144,12 @@ module ApplicationTests
assert_equal "Rack::Config", middleware.second
end
+ test 'unshift middleware' do
+ add_to_config 'config.middleware.unshift Rack::Config'
+ boot!
+ assert_equal 'Rack::Config', middleware.first
+ end
+
test "Rails.cache does not respond to middleware" do
add_to_config "config.cache_store = :memory_store"
boot!
diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb
index 20afca2618..ba688f1e9e 100644
--- a/railties/test/commands/server_test.rb
+++ b/railties/test/commands/server_test.rb
@@ -27,26 +27,62 @@ class Rails::ServerTest < ActiveSupport::TestCase
end
def test_environment_with_rails_env
- with_rails_env 'production' do
- server = Rails::Server.new
- assert_equal 'production', server.options[:environment]
+ with_rack_env nil do
+ with_rails_env 'production' do
+ server = Rails::Server.new
+ assert_equal 'production', server.options[:environment]
+ end
end
end
def test_environment_with_rack_env
- with_rack_env 'production' do
- server = Rails::Server.new
- assert_equal 'production', server.options[:environment]
+ with_rails_env nil do
+ with_rack_env 'production' do
+ server = Rails::Server.new
+ assert_equal 'production', server.options[:environment]
+ end
end
end
def test_log_stdout
- args = ["-e", "development"]
- options = Rails::Server::Options.new.parse!(args)
- assert_equal true, options[:log_stdout]
+ with_rack_env nil do
+ with_rails_env nil do
+ args = []
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal true, options[:log_stdout]
- args = ["-e", "production"]
- options = Rails::Server::Options.new.parse!(args)
- assert_equal false, options[:log_stdout]
+ args = ["-e", "development"]
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal true, options[:log_stdout]
+
+ args = ["-e", "production"]
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal false, options[:log_stdout]
+
+ with_rack_env 'development' do
+ args = []
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal true, options[:log_stdout]
+ end
+
+ with_rack_env 'production' do
+ args = []
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal false, options[:log_stdout]
+ end
+
+ with_rails_env 'development' do
+ args = []
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal true, options[:log_stdout]
+ end
+
+ with_rails_env 'production' do
+ args = []
+ options = Rails::Server::Options.new.parse!(args)
+ assert_equal false, options[:log_stdout]
+ end
+ end
+ end
end
end
diff --git a/railties/test/env_helpers.rb b/railties/test/env_helpers.rb
index 6223c85bbf..330fe150ca 100644
--- a/railties/test/env_helpers.rb
+++ b/railties/test/env_helpers.rb
@@ -1,7 +1,10 @@
+require 'rails'
+
module EnvHelpers
private
def with_rails_env(env)
+ Rails.instance_variable_set :@_env, nil
switch_env 'RAILS_ENV', env do
switch_env 'RACK_ENV', nil do
yield
@@ -10,6 +13,7 @@ module EnvHelpers
end
def with_rack_env(env)
+ Rails.instance_variable_set :@_env, nil
switch_env 'RACK_ENV', env do
switch_env 'RAILS_ENV', nil do
yield