aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/CHANGELOG.md80
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb2
-rw-r--r--actionpack/lib/action_controller/log_subscriber.rb3
-rw-r--r--actionpack/lib/action_controller/test_case.rb71
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/exception_wrapper.rb8
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb4
-rw-r--r--actionpack/lib/action_view/test_case.rb6
-rw-r--r--actionpack/lib/sprockets/helpers/rails_helper.rb4
-rw-r--r--actionpack/lib/sprockets/static_compiler.rb32
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb22
-rw-r--r--actionpack/test/controller/log_subscriber_test.rb15
-rw-r--r--actionpack/test/controller/render_test.rb11
-rw-r--r--actionpack/test/controller/test_test.rb6
-rw-r--r--actionpack/test/dispatch/routing_test.rb27
-rw-r--r--actionpack/test/dispatch/uploaded_file_test.rb2
-rw-r--r--actionpack/test/fixtures/test/hello/hello.erb1
-rw-r--r--actionpack/test/template/sprockets_helper_test.rb8
-rw-r--r--actionpack/test/template/url_helper_test.rb7
-rw-r--r--activemodel/CHANGELOG.md13
-rw-r--r--activemodel/lib/active_model/validations.rb1
-rw-r--r--activemodel/test/cases/serializers/xml_serialization_test.rb2
-rw-r--r--activerecord/CHANGELOG.md87
-rw-r--r--activerecord/lib/active_record/associations.rb12
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb6
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb14
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb24
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb6
-rw-r--r--activerecord/lib/active_record/base.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb4
-rw-r--r--activerecord/lib/active_record/counter_cache.rb4
-rw-r--r--activerecord/lib/active_record/explain_subscriber.rb3
-rw-r--r--activerecord/lib/active_record/railties/databases.rake12
-rw-r--r--activerecord/lib/active_record/scoping/named.rb4
-rw-r--r--activerecord/lib/active_record/timestamp.rb1
-rw-r--r--activerecord/lib/active_record/transactions.rb5
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb74
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb4
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb38
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb11
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb8
-rw-r--r--activerecord/test/cases/base_test.rb12
-rw-r--r--activerecord/test/cases/column_test.rb24
-rw-r--r--activerecord/test/cases/connection_pool_test.rb22
-rw-r--r--activerecord/test/cases/counter_cache_test.rb15
-rw-r--r--activerecord/test/cases/defaults_test.rb40
-rw-r--r--activerecord/test/cases/dirty_test.rb16
-rw-r--r--activerecord/test/cases/dup_test.rb15
-rw-r--r--activerecord/test/cases/explain_subscriber_test.rb9
-rw-r--r--activerecord/test/cases/pooled_connections_test.rb4
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb4
-rw-r--r--activerecord/test/cases/relations_test.rb4
-rw-r--r--activerecord/test/cases/transactions_test.rb15
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb12
-rw-r--r--activerecord/test/config.example.yml2
-rw-r--r--activerecord/test/models/subscription.rb2
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb9
-rw-r--r--activerecord/test/schema/schema.rb1
-rw-r--r--activesupport/CHANGELOG.md18
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb46
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb6
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb4
-rw-r--r--activesupport/test/tagged_logging_test.rb27
-rw-r--r--activesupport/test/time_zone_test.rb8
-rw-r--r--railties/CHANGELOG.md4
-rw-r--r--railties/guides/source/3_1_release_notes.textile122
-rw-r--r--railties/guides/source/getting_started.textile2
-rw-r--r--railties/lib/rails/application.rb2
-rw-r--r--railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb45
-rw-r--r--railties/lib/rails/rack/logger.rb39
-rw-r--r--railties/lib/rails/ruby_version_check.rb4
-rw-r--r--railties/test/application/assets_test.rb24
-rw-r--r--railties/test/application/rack/logger_test.rb12
-rw-r--r--railties/test/generators/namespaced_generators_test.rb76
-rw-r--r--railties/test/generators/scaffold_generator_test.rb2
82 files changed, 1187 insertions, 201 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 91274f35ab..1d38e33874 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,85 @@
## Rails 3.2.9 (unreleased) ##
+* Accept :remote as symbolic option for `link_to` helper. *Riley Lynch*
+
+* Warn when the `:locals` option is passed to `assert_template` outside of a view test case
+ Fix #3415
+
+ *Yves Senn*
+
+* Rename internal variables on ActionController::TemplateAssertions to prevent
+ naming collisions. @partials, @templates and @layouts are now prefixed with an underscore.
+ Fix #7459
+
+ *Yves Senn*
+
+* `resource` and `resources` don't modify the passed options hash
+ Fix #7777
+
+ *Yves Senn*
+
+* Precompiled assets include aliases from foo.js to foo/index.js and vice versa.
+
+ # Precompiles phone-<digest>.css and aliases phone/index.css to phone.css.
+ config.assets.precompile = [ 'phone.css' ]
+
+ # Precompiles phone/index-<digest>.css and aliases phone.css to phone/index.css.
+ config.assets.precompile = [ 'phone/index.css' ]
+
+ # Both of these work with either precompile thanks to their aliases.
+ <%= stylesheet_link_tag 'phone', media: 'all' %>
+ <%= stylesheet_link_tag 'phone/index', media: 'all' %>
+
+ *Jeremy Kemper*
+
+* `assert_template` is no more passing with what ever string that matches
+ with the template name.
+
+ Before when we have a template `/layout/hello.html.erb`, `assert_template`
+ was passing with any string that matches. This behavior allowed false
+ positive like:
+
+ assert_template "layout"
+ assert_template "out/hello"
+
+ Now it only passes with:
+
+ assert_template "layout/hello"
+ assert_template "hello"
+
+ Fixes #3849.
+
+ *Hugolnx*
+
+* Handle `ActionDispatch::Http::UploadedFile` like `Rack::Test::UploadedFile`, don't call to_param on it. Since
+ `Rack::Test::UploadedFile` isn't API compatible this is needed to test file uploads that rely on `tempfile`
+ being available.
+
+ *Tim Vandecasteele*
+
+* Fixed a bug with shorthand routes scoped with the `:module` option not
+ adding the module to the controller as described in issue #6497.
+ This should now work properly:
+
+ scope :module => "engine" do
+ get "api/version" # routes to engine/api#version
+ end
+
+ *Luiz Felipe Garcia Pereira*
+
+* Respect `config.digest = false` for `asset_path`
+
+ Previously, the `asset_path` internals only respected the `:digest`
+ option, but ignored the global config setting. This meant that
+ `config.digest = false` could not be used in conjunction with
+ `config.compile = false` this corrects the behavior.
+
+ *Peter Wagenet*
+
+* Fix #7646, the log now displays the correct status code when an exception is raised.
+
+ *Yves Senn*
+
* Fix handling of date selects when using both disabled and discard options.
Fixes #7431.
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 3d67541557..002351696d 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
s.add_dependency('rack', '~> 1.4.0')
s.add_dependency('rack-test', '~> 0.6.1')
s.add_dependency('journey', '~> 1.0.4')
- s.add_dependency('sprockets', '~> 2.1')
+ s.add_dependency('sprockets', '~> 2.2')
s.add_dependency('erubis', '~> 2.7.0')
s.add_development_dependency('tzinfo', '~> 0.3.29')
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index c482062592..3b07e4cdba 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -309,7 +309,7 @@ module AbstractController
# still does a dynamic lookup. In next Rails release, we should @_layout
# to be inheritable so we can skip the child lookup if the parent explicitly
# set the layout.
- parent = self.superclass.instance_variable_get(:@_layout)
+ parent = self.superclass.instance_eval { @_layout if defined?(@_layout) }
@_layout = nil
inspect = parent.is_a?(Proc) ? parent.inspect : parent
diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb
index 4c76f4c43b..194f26aefc 100644
--- a/actionpack/lib/action_controller/log_subscriber.rb
+++ b/actionpack/lib/action_controller/log_subscriber.rb
@@ -20,7 +20,8 @@ module ActionController
status = payload[:status]
if status.nil? && payload[:exception].present?
- status = Rack::Utils.status_code(ActionDispatch::ExceptionWrapper.new({}, payload[:exception]).status_code)
+ exception_class_name = payload[:exception].first
+ status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
end
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration
message << " (#{additions.join(" | ")})" unless additions.blank?
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 05e3cd40b5..d486f32c01 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -14,13 +14,13 @@ module ActionController
end
def setup_subscriptions
- @partials = Hash.new(0)
- @templates = Hash.new(0)
- @layouts = Hash.new(0)
+ @_partials = Hash.new(0)
+ @_templates = Hash.new(0)
+ @_layouts = Hash.new(0)
ActiveSupport::Notifications.subscribe("render_template.action_view") do |name, start, finish, id, payload|
path = payload[:layout]
- @layouts[path] += 1
+ @_layouts[path] += 1
end
ActiveSupport::Notifications.subscribe("!render_template.action_view") do |name, start, finish, id, payload|
@@ -28,11 +28,11 @@ module ActionController
next unless path
partial = path =~ /^.*\/_[^\/]*$/
if partial
- @partials[path] += 1
- @partials[path.split("/").last] += 1
- @templates[path] += 1
+ @_partials[path] += 1
+ @_partials[path.split("/").last] += 1
+ @_templates[path] += 1
else
- @templates[path] += 1
+ @_templates[path] += 1
end
end
end
@@ -43,9 +43,9 @@ module ActionController
end
def process(*args)
- @partials = Hash.new(0)
- @templates = Hash.new(0)
- @layouts = Hash.new(0)
+ @_partials = Hash.new(0)
+ @_templates = Hash.new(0)
+ @_layouts = Hash.new(0)
super
end
@@ -72,43 +72,54 @@ module ActionController
validate_request!
case options
- when NilClass, String, Symbol
+ when NilClass, Regexp, String, Symbol
options = options.to_s if Symbol === options
- rendered = @templates
+ rendered = @_templates
msg = build_message(message,
"expecting <?> but rendering with <?>",
options, rendered.keys.join(', '))
- assert_block(msg) do
- if options
+ matches_template =
+ case options
+ when String
+ rendered.any? do |t, num|
+ options_splited = options.split(File::SEPARATOR)
+ t_splited = t.split(File::SEPARATOR)
+ t_splited.last(options_splited.size) == options_splited
+ end
+ when Regexp
rendered.any? { |t,num| t.match(options) }
- else
- @templates.blank?
+ when NilClass
+ rendered.blank?
end
- end
+ assert matches_template, msg
when Hash
if expected_layout = options[:layout]
msg = build_message(message,
"expecting layout <?> but action rendered <?>",
- expected_layout, @layouts.keys)
+ expected_layout, @_layouts.keys)
case expected_layout
when String
- assert(@layouts.keys.include?(expected_layout), msg)
+ assert(@_layouts.keys.include?(expected_layout), msg)
when Regexp
- assert(@layouts.keys.any? {|l| l =~ expected_layout }, msg)
+ assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg)
when nil
- assert(@layouts.empty?, msg)
+ assert(@_layouts.empty?, msg)
end
end
if expected_partial = options[:partial]
if expected_locals = options[:locals]
- actual_locals = @locals[expected_partial.to_s.sub(/^_/,'')]
- expected_locals.each_pair do |k,v|
- assert_equal(v, actual_locals[k])
+ if defined?(@locals)
+ actual_locals = @locals[expected_partial.to_s.sub(/^_/,'')]
+ expected_locals.each_pair do |k,v|
+ assert_equal(v, actual_locals[k])
+ end
+ else
+ warn "the :locals option to #assert_template is only supported in a ActionView::TestCase"
end
elsif expected_count = options[:count]
- actual_count = @partials[expected_partial]
+ actual_count = @_partials[expected_partial]
msg = build_message(message,
"expecting ? to be rendered ? time(s) but rendered ? time(s)",
expected_partial, expected_count, actual_count)
@@ -116,11 +127,11 @@ module ActionController
else
msg = build_message(message,
"expecting partial <?> but action rendered <?>",
- options[:partial], @partials.keys)
- assert(@partials.include?(expected_partial), msg)
+ options[:partial], @_partials.keys)
+ assert(@_partials.include?(expected_partial), msg)
end
else
- assert @partials.empty?,
+ assert @_partials.empty?,
"Expected no partials to be rendered"
end
end
@@ -422,7 +433,7 @@ module ActionController
Hash[hash_or_array_or_value.map{|key, value| [key, paramify_values(value)] }]
when Array
hash_or_array_or_value.map {|i| paramify_values(i)}
- when Rack::Test::UploadedFile
+ when Rack::Test::UploadedFile, ActionDispatch::Http::UploadedFile
hash_or_array_or_value
else
hash_or_array_or_value.to_param
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index 94fa747a79..eb95f79e0b 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -19,7 +19,7 @@ module ActionDispatch
[:open, :path, :rewind, :size].each do |method|
class_eval "def #{method}; @tempfile.#{method}; end"
end
-
+
private
def encode_filename(filename)
# Encode the filename in the utf8 encoding, unless it is nil or we're in 1.8
diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
index c0532c80c4..0f49dc65bf 100644
--- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
+++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
@@ -34,7 +34,7 @@ module ActionDispatch
end
def status_code
- Rack::Utils.status_code(@@rescue_responses[@exception.class.name])
+ self.class.status_code_for_exception(@exception.class.name)
end
def application_trace
@@ -49,6 +49,10 @@ module ActionDispatch
clean_backtrace(:all)
end
+ def self.status_code_for_exception(class_name)
+ Rack::Utils.status_code(@@rescue_responses[class_name])
+ end
+
private
def original_exception(exception)
@@ -75,4 +79,4 @@ module ActionDispatch
@backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 40ff69693b..cd2f464506 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -166,7 +166,7 @@ module ActionDispatch
controller ||= default_controller
action ||= default_action
- unless controller.is_a?(Regexp) || to_shorthand
+ unless controller.is_a?(Regexp)
controller = [@scope[:module], controller].compact.join("/").presence
end
@@ -982,7 +982,7 @@ module ActionDispatch
# === Options
# Takes same options as +resources+.
def resource(*resources, &block)
- options = resources.extract_options!
+ options = resources.extract_options!.dup
if apply_common_behavior_for(:resource, resources, options, &block)
return self
@@ -1120,7 +1120,7 @@ module ActionDispatch
# # resource actions are at /admin/posts.
# resources :posts, :path => "admin/posts"
def resources(*resources, &block)
- options = resources.extract_options!
+ options = resources.extract_options!.dup
if apply_common_behavior_for(:resources, resources, options, &block)
return self
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 1f1cd3cee3..812bb4de9e 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -633,7 +633,9 @@ module ActionView
end
def link_to_remote_options?(options)
- options.is_a?(Hash) && options.key?('remote') && options.delete('remote')
+ if options.is_a?(Hash)
+ options.delete('remote') || options.delete(:remote)
+ end
end
def add_method_to_attributes!(html_options, method)
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 658a503f7f..dfaa1d88e4 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -193,16 +193,16 @@ module ActionView
@_result
@_routes
@controller
- @layouts
+ @_layouts
@locals
@method_name
@output_buffer
- @partials
+ @_partials
@passed
@rendered
@request
@routes
- @templates
+ @_templates
@options
@test_passed
@view
diff --git a/actionpack/lib/sprockets/helpers/rails_helper.rb b/actionpack/lib/sprockets/helpers/rails_helper.rb
index 8f0e0f8ee1..690c71b472 100644
--- a/actionpack/lib/sprockets/helpers/rails_helper.rb
+++ b/actionpack/lib/sprockets/helpers/rails_helper.rb
@@ -147,7 +147,9 @@ module Sprockets
if source[0] == ?/
source
else
- source = digest_for(source) unless options[:digest] == false
+ if digest_assets && options[:digest] != false
+ source = digest_for(source)
+ end
source = File.join(dir, source)
source = "/#{source}" unless source =~ /^\//
source
diff --git a/actionpack/lib/sprockets/static_compiler.rb b/actionpack/lib/sprockets/static_compiler.rb
index 2e2db4b760..4341a27d5d 100644
--- a/actionpack/lib/sprockets/static_compiler.rb
+++ b/actionpack/lib/sprockets/static_compiler.rb
@@ -15,13 +15,11 @@ module Sprockets
def compile
manifest = {}
- env.each_logical_path do |logical_path|
- if File.basename(logical_path)[/[^\.]+/, 0] == 'index'
- logical_path.sub!(/\/index\./, '.')
- end
- next unless compile_path?(logical_path)
+ env.each_logical_path(paths) do |logical_path|
if asset = env.find_asset(logical_path)
- manifest[logical_path] = write_asset(asset)
+ digest_path = write_asset(asset)
+ manifest[asset.logical_path] = digest_path
+ manifest[aliased_path_for(asset.logical_path)] = digest_path
end
end
write_manifest(manifest) if @manifest
@@ -43,22 +41,16 @@ module Sprockets
end
end
- def compile_path?(logical_path)
- paths.each do |path|
- case path
- when Regexp
- return true if path.match(logical_path)
- when Proc
- return true if path.call(logical_path)
- else
- return true if File.fnmatch(path.to_s, logical_path)
- end
- end
- false
- end
-
def path_for(asset)
@digest ? asset.digest_path : asset.logical_path
end
+
+ def aliased_path_for(logical_path)
+ if File.basename(logical_path).start_with?('index')
+ logical_path.sub(/\/index([^\/]+)$/, '\1')
+ else
+ logical_path.sub(/\.([^\/]+)$/, '/index.\1')
+ end
+ end
end
end
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index 5252e43c25..81cf096952 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -7,6 +7,7 @@ class ActionPackAssertionsController < ActionController::Base
def nothing() head :ok end
def hello_world() render :template => "test/hello_world"; end
+ def hello_repeating_in_path() render :template => "test/hello/hello"; end
def hello_xml_world() render :template => "test/hello_xml_world"; end
@@ -469,6 +470,20 @@ class AssertTemplateTest < ActionController::TestCase
end
end
+ def test_fails_with_incorrect_string_that_matches
+ get :hello_world
+ assert_raise(ActiveSupport::TestCase::Assertion) do
+ assert_template 'est/he'
+ end
+ end
+
+ def test_fails_with_repeated_name_in_path
+ get :hello_repeating_in_path
+ assert_raise(ActiveSupport::TestCase::Assertion) do
+ assert_template 'test/hello'
+ end
+ end
+
def test_fails_with_incorrect_symbol
get :hello_world
assert_raise(ActiveSupport::TestCase::Assertion) do
@@ -476,6 +491,13 @@ class AssertTemplateTest < ActionController::TestCase
end
end
+ def test_fails_with_incorrect_symbol_that_matches
+ get :hello_world
+ assert_raise(ActiveSupport::TestCase::Assertion) do
+ assert_template :"est/he"
+ end
+ end
+
def test_fails_with_wrong_layout
get :render_with_layout
assert_raise(ActiveSupport::TestCase::Assertion) do
diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb
index 700fd788fa..a72b6dde1a 100644
--- a/actionpack/test/controller/log_subscriber_test.rb
+++ b/actionpack/test/controller/log_subscriber_test.rb
@@ -54,6 +54,10 @@ module Another
def with_rescued_exception
raise SpecialException
end
+
+ def with_action_not_found
+ raise AbstractController::ActionNotFound
+ end
end
end
@@ -225,6 +229,17 @@ class ACLogSubscriberTest < ActionController::TestCase
assert_match(/Completed 406/, logs.last)
end
+ def test_process_action_with_with_action_not_found_logs_404
+ begin
+ get :with_action_not_found
+ wait
+ rescue AbstractController::ActionNotFound
+ end
+
+ assert_equal 2, logs.size
+ assert_match(/Completed 404/, logs.last)
+ end
+
def logs
@logs ||= @logger.logged(:info)
end
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index afaf9e5ea9..8c631e218f 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -1405,6 +1405,17 @@ class RenderTest < ActionController::TestCase
assert_equal "Bonjour: davidBonjour: mary", @response.body
end
+ def test_locals_option_to_assert_template_is_not_supported
+ warning_buffer = StringIO.new
+ $stderr = warning_buffer
+
+ get :partial_collection_with_locals
+ assert_template :partial => 'customer_greeting', :locals => { :greeting => 'Bonjour' }
+ assert_equal "the :locals option to #assert_template is only supported in a ActionView::TestCase\n", warning_buffer.string
+ ensure
+ $stderr = STDERR
+ end
+
def test_partial_collection_with_spacer
get :partial_collection_with_spacer
assert_equal "Hello: davidonly partialHello: mary", @response.body
diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb
index 6a5843f9d7..1da5298900 100644
--- a/actionpack/test/controller/test_test.rb
+++ b/actionpack/test/controller/test_test.rb
@@ -766,6 +766,12 @@ XML
assert_equal '159528', @response.body
end
+ def test_action_dispatch_uploaded_file_upload
+ tf = Class.new { def size; 159528 end }
+ post :test_file_upload, :file => ActionDispatch::Http::UploadedFile.new(:tempfile => tf.new)
+ assert_equal '159528', @response.body
+ end
+
def test_test_uploaded_file_exception_when_file_doesnt_exist
assert_raise(RuntimeError) { Rack::Test::UploadedFile.new('non_existent_file') }
end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 00c71dc8be..f32b1b9c71 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -362,6 +362,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resources :errors, :shallow => true do
resources :notices
end
+ get 'api/version'
end
scope :path => 'api' do
@@ -1185,6 +1186,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_resource_does_not_modify_passed_options
+ options = {:id => /.+?/, :format => /json|xml/}
+ self.class.stub_controllers do |routes|
+ routes.draw do
+ resource :user, options
+ end
+ end
+ assert_equal({:id => /.+?/, :format => /json|xml/}, options)
+ end
+
+ def test_resources_does_not_modify_passed_options
+ options = {:id => /.+?/, :format => /json|xml/}
+ self.class.stub_controllers do |routes|
+ routes.draw do
+ resources :users, options
+ end
+ end
+ assert_equal({:id => /.+?/, :format => /json|xml/}, options)
+ end
+
def test_path_names
with_test_routes do
get '/pt/projetos'
@@ -1376,6 +1397,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_match_shorthand_with_module
+ assert_equal '/api/version', api_version_path
+ get '/api/version'
+ assert_equal 'api/api#version', @response.body
+ end
+
def test_dynamically_generated_helpers_on_collection_do_not_clobber_resources_url_helper
with_test_routes do
assert_equal '/replies', replies_path
diff --git a/actionpack/test/dispatch/uploaded_file_test.rb b/actionpack/test/dispatch/uploaded_file_test.rb
index 7e4a1519fb..fae39a403e 100644
--- a/actionpack/test/dispatch/uploaded_file_test.rb
+++ b/actionpack/test/dispatch/uploaded_file_test.rb
@@ -12,7 +12,7 @@ module ActionDispatch
uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new)
assert_equal 'foo', uf.original_filename
end
-
+
if "ruby".encoding_aware?
def test_filename_should_be_in_utf_8
uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new)
diff --git a/actionpack/test/fixtures/test/hello/hello.erb b/actionpack/test/fixtures/test/hello/hello.erb
new file mode 100644
index 0000000000..6769dd60bd
--- /dev/null
+++ b/actionpack/test/fixtures/test/hello/hello.erb
@@ -0,0 +1 @@
+Hello world! \ No newline at end of file
diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb
index b908b6777c..72d03e43e9 100644
--- a/actionpack/test/template/sprockets_helper_test.rb
+++ b/actionpack/test/template/sprockets_helper_test.rb
@@ -360,4 +360,12 @@ class SprocketsHelperTest < ActionView::TestCase
assert_equal '/assets/logo.png',
asset_path("logo.png")
end
+
+ test "`config.digest = false` works with `config.compile = false`" do
+ @config.assets.digest = false
+ @config.assets.compile = false
+
+ assert_equal '/assets/logo.png',
+ asset_path("logo.png")
+ end
end
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index d55b1fd2bd..38f77203e0 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -227,6 +227,13 @@ class UrlHelperTest < ActiveSupport::TestCase
)
end
+ def test_link_to_with_symbolic_remote_in_non_html_options
+ assert_dom_equal(
+ "<a href=\"/\" data-remote=\"true\">Hello</a>",
+ link_to("Hello", hash_for([:remote, true]), {})
+ )
+ end
+
def test_link_tag_using_post_javascript
assert_dom_equal(
"<a href='http://www.example.com' data-method=\"post\" rel=\"nofollow\">Hello</a>",
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 2a3c51172b..d3056e73a2 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,3 +1,16 @@
+## Rails 3.2.9 (unreleased)
+
+* Due to a change in builder, nil values and empty strings now generates
+ closed tags, so instead of this:
+
+ <pseudonyms nil=\"true\"></pseudonyms>
+
+ It generates this:
+
+ <pseudonyms nil=\"true\"/>
+
+ *Carlos Antonio da Silva*
+
## Rails 3.2.8 (Aug 9, 2012) ##
* No changes.
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index f38121c175..45d8677fa0 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -5,6 +5,7 @@ require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/hash/except'
require 'active_model/errors'
require 'active_model/validations/callbacks'
+require 'active_model/validator'
module ActiveModel
diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb
index 38aecf51ff..7f14b21cf4 100644
--- a/activemodel/test/cases/serializers/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializers/xml_serialization_test.rb
@@ -132,7 +132,7 @@ class XmlSerializationTest < ActiveModel::TestCase
end
test "should serialize nil" do
- assert_match %r{<pseudonyms nil=\"true\"></pseudonyms>}, @contact.to_xml(:methods => :pseudonyms)
+ assert_match %r{<pseudonyms nil=\"true\"/>}, @contact.to_xml(:methods => :pseudonyms)
end
test "should serialize integer" do
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index c836886c08..aafdbec25c 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,92 @@
## Rails 3.2.9 (unreleased)
+* Fix AR#dup to nullify the validation errors in the dup'ed object. Previously the original
+ and the dup'ed object shared the same errors.
+
+ * Christian Seiler*
+
+* Synchronize around deleting from the reserved connections hash.
+ Fixes #7955
+
+* PostgreSQL adapter correctly fetches default values when using
+ multiple schemas and domains in a db. Fixes #7914
+
+ *Arturo Pie*
+
+* Fix deprecation notice when loading a collection association that
+ selects columns from other tables, if a new record was previously
+ built using that association.
+
+ *Ernie Miller*
+
+* The postgres adapter now supports tables with capital letters.
+ Fix #5920
+
+ *Yves Senn*
+
+* `CollectionAssociation#count` returns `0` without querying if the
+ parent record is not persisted.
+
+ Before:
+
+ person.pets.count
+ # SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" IS NULL
+ # => 0
+
+ After:
+
+ person.pets.count
+ # fires without sql query
+ # => 0
+
+ *Francesco Rodriguez*
+
+* Fix `reset_counters` crashing on `has_many :through` associations.
+ Fix #7822.
+
+ *lulalala*
+
+* ConnectionPool recognizes checkout_timeout spec key as taking
+ precedence over legacy wait_timeout spec key, can be used to avoid
+ conflict with mysql2 use of wait_timeout. Closes #7684.
+
+ *jrochkind*
+
+* Rename field_changed? to _field_changed? so that users can create a field named field
+
+ *Akira Matsuda*, backported by *Steve Klabnik*
+
+* Fix creation of through association models when using `collection=[]`
+ on a `has_many :through` association from an unsaved model.
+ Fix #7661.
+
+ *Ernie Miller*
+
+* Explain only normal CRUD sql (select / update / insert / delete).
+ Fix problem that explains unexplainable sql. Closes #7544 #6458.
+
+ *kennyj*
+
+* Backport test coverage to ensure that PostgreSQL auto-reconnect functionality
+ remains healthy.
+
+ *Steve Jorgensen*
+
+* Use config['encoding'] instead of config['charset'] when executing
+ databases.rake in the mysql/mysql2. A correct option for a database.yml
+ is 'encoding'.
+
+ *kennyj*
+
+* Fix ConnectionAdapters::Column.type_cast_code integer conversion,
+ to always convert values to integer calling #to_i. Fixes #7509.
+
+ *Thiago Pradi*
+
+* Fix time column type casting for invalid time string values to correctly return nil.
+
+ *Adam Meehan*
+
* Fix `becomes` when using a configured `inheritance_column`.
*Yves Senn*
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 5aa9e26fa6..b74ff62c09 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -135,11 +135,13 @@ module ActiveRecord
autoload :HasAndBelongsToMany, 'active_record/associations/builder/has_and_belongs_to_many'
end
- autoload :Preloader, 'active_record/associations/preloader'
- autoload :JoinDependency, 'active_record/associations/join_dependency'
- autoload :AssociationScope, 'active_record/associations/association_scope'
- autoload :AliasTracker, 'active_record/associations/alias_tracker'
- autoload :JoinHelper, 'active_record/associations/join_helper'
+ eager_autoload do
+ autoload :Preloader, 'active_record/associations/preloader'
+ autoload :JoinDependency, 'active_record/associations/join_dependency'
+ autoload :AssociationScope, 'active_record/associations/association_scope'
+ autoload :AliasTracker, 'active_record/associations/alias_tracker'
+ autoload :JoinHelper, 'active_record/associations/join_helper'
+ end
# Clears out the association cache.
def clear_association_cache #:nodoc:
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index d929a45c92..2c852f6efc 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -190,6 +190,8 @@ module ActiveRecord
# association, it will be used for the query. Otherwise, construct options and pass them with
# scope to the target class's +count+.
def count(column_name = nil, count_options = {})
+ return 0 if owner.new_record?
+
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
if options[:counter_sql] || options[:finder_sql]
@@ -362,7 +364,7 @@ module ActiveRecord
# replace the SELECT clause with COUNT(SELECTS), preserving any hints within /* ... */
interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) do
count_with = $2.to_s
- count_with = '*' if count_with.blank? || count_with =~ /,/
+ count_with = '*' if count_with.blank? || count_with =~ /,/ || count_with =~ /\.\*/
"SELECT #{$1}COUNT(#{count_with}) FROM"
end
end
@@ -407,7 +409,7 @@ module ActiveRecord
if mem_index
mem_record = memory.delete_at(mem_index)
- (record.attribute_names - mem_record.changes.keys).each do |name|
+ ((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name|
mem_record[name] = record[name]
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 53d49fef2e..ce81333aa9 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -38,6 +38,20 @@ module ActiveRecord
super
end
+ def concat_records(records)
+ ensure_not_nested
+
+ records = super
+
+ if owner.new_record? && records
+ records.flatten.each do |record|
+ build_through_record(record)
+ end
+ end
+
+ records
+ end
+
def insert_record(record, validate = true, raise = false)
ensure_not_nested
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index fafed94ff2..12cfdd4622 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -30,17 +30,21 @@ module ActiveRecord
# option references an association's column), it will fallback to the table
# join strategy.
class Preloader #:nodoc:
- autoload :Association, 'active_record/associations/preloader/association'
- autoload :SingularAssociation, 'active_record/associations/preloader/singular_association'
- autoload :CollectionAssociation, 'active_record/associations/preloader/collection_association'
- autoload :ThroughAssociation, 'active_record/associations/preloader/through_association'
+ extend ActiveSupport::Autoload
- autoload :HasMany, 'active_record/associations/preloader/has_many'
- autoload :HasManyThrough, 'active_record/associations/preloader/has_many_through'
- autoload :HasOne, 'active_record/associations/preloader/has_one'
- autoload :HasOneThrough, 'active_record/associations/preloader/has_one_through'
- autoload :HasAndBelongsToMany, 'active_record/associations/preloader/has_and_belongs_to_many'
- autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
+ eager_autoload do
+ autoload :Association, 'active_record/associations/preloader/association'
+ autoload :SingularAssociation, 'active_record/associations/preloader/singular_association'
+ autoload :CollectionAssociation, 'active_record/associations/preloader/collection_association'
+ autoload :ThroughAssociation, 'active_record/associations/preloader/through_association'
+
+ autoload :HasMany, 'active_record/associations/preloader/has_many'
+ autoload :HasManyThrough, 'active_record/associations/preloader/has_many_through'
+ autoload :HasOne, 'active_record/associations/preloader/has_one'
+ autoload :HasOneThrough, 'active_record/associations/preloader/has_one_through'
+ autoload :HasAndBelongsToMany, 'active_record/associations/preloader/has_and_belongs_to_many'
+ autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
+ end
attr_reader :records, :associations, :options, :model
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index ed863a9696..9cd8954496 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -55,12 +55,12 @@ module ActiveRecord
# The attribute already has an unsaved change.
if attribute_changed?(attr)
old = @changed_attributes[attr]
- @changed_attributes.delete(attr) unless field_changed?(attr, old, value)
+ @changed_attributes.delete(attr) unless _field_changed?(attr, old, value)
else
old = clone_attribute_value(:read_attribute, attr)
# Save Time objects as TimeWithZone if time_zone_aware_attributes == true
old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
- @changed_attributes[attr] = old if field_changed?(attr, old, value)
+ @changed_attributes[attr] = old if _field_changed?(attr, old, value)
end
# Carry on.
@@ -77,7 +77,7 @@ module ActiveRecord
end
end
- def field_changed?(attr, old, value)
+ def _field_changed?(attr, old, value)
if column = column_for_attribute(attr)
if column.number? && (changes_from_nil_to_empty_string?(column, old, value) ||
changes_from_zero_to_string?(old, value))
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index b51bb5cc5e..62c8110274 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -450,12 +450,12 @@ module ActiveRecord #:nodoc:
private
def relation #:nodoc:
- @relation ||= Relation.new(self, arel_table)
+ relation = Relation.new(self, arel_table)
if finder_needs_type_condition?
- @relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
+ relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
else
- @relation
+ relation
end
end
end
@@ -489,7 +489,6 @@ module ActiveRecord #:nodoc:
@marked_for_destruction = false
@previously_changed = {}
@changed_attributes = {}
- @relation = nil
ensure_proper_type
@@ -544,7 +543,7 @@ module ActiveRecord #:nodoc:
@changed_attributes = {}
self.class.column_defaults.each do |attr, orig_value|
- @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr])
+ @changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr])
end
@aggregation_cache = {}
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index d4649102df..6cb97bc550 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -54,8 +54,11 @@ module ActiveRecord
# your database connection configuration:
#
# * +pool+: number indicating size of connection pool (default 5)
- # * +wait_timeout+: number of seconds to block and wait for a connection
- # before giving up and raising a timeout error (default 5 seconds).
+ # * +checkout _timeout+: number of seconds to block and wait for a
+ # connection before giving up and raising a timeout error
+ # (default 5 seconds). ('wait_timeout' supported for backwards
+ # compatibility, but conflicts with key used for different purpose
+ # by mysql2 adapter).
class ConnectionPool
include MonitorMixin
@@ -77,7 +80,10 @@ module ActiveRecord
@reserved_connections = {}
@queue = new_cond
- @timeout = spec.config[:wait_timeout] || 5
+ # 'wait_timeout', the backward-compatible key, conflicts with spec key
+ # used by mysql2 for something entirely different, checkout_timeout
+ # preferred to avoid conflict and allow independent values.
+ @timeout = spec.config[:checkout_timeout] || spec.config[:wait_timeout] || 5
# default max pool size to 5
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
@@ -110,7 +116,7 @@ module ActiveRecord
# #release_connection releases the connection-thread association
# and returns the connection to the pool.
def release_connection(with_id = current_connection_id)
- conn = @reserved_connections.delete(with_id)
+ conn = synchronize { @reserved_connections.delete(with_id) }
checkin conn if conn
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 393a3b623a..d38e8464c5 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -75,7 +75,7 @@ module ActiveRecord
case type
when :string, :text then value
- when :integer then value.to_i rescue value ? 1 : 0
+ when :integer then value.to_i
when :float then value.to_f
when :decimal then klass.value_to_decimal(value)
when :datetime, :timestamp then klass.string_to_time(value)
@@ -92,7 +92,7 @@ module ActiveRecord
case type
when :string, :text then var_name
- when :integer then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
+ when :integer then "(#{var_name}.to_i)"
when :float then "#{var_name}.to_f"
when :decimal then "#{klass}.value_to_decimal(#{var_name})"
when :datetime, :timestamp then "#{klass}.string_to_time(#{var_name})"
@@ -150,7 +150,13 @@ module ActiveRecord
return string unless string.is_a?(String)
return nil if string.empty?
- string_to_time "2000-01-01 #{string}"
+ dummy_time_string = "2000-01-01 #{string}"
+
+ fast_string_to_time(dummy_time_string) || begin
+ time_hash = Date._parse(dummy_time_string)
+ return nil if time_hash[:hour].nil?
+ new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
+ end
end
# convert something to a boolean
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 3dfd150d5a..80a0c6d13c 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -145,11 +145,8 @@ module ActiveRecord
when /\A\(?(-?\d+(\.\d*)?\)?)\z/
$1
# Character types
- when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
+ when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
$1
- # Character types (8.1 formatting)
- when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
- $1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
# Binary data types
when /\A'(.*)'::bytea\z/m
$1
@@ -354,6 +351,7 @@ module ActiveRecord
def reconnect!
clear_cache!
@connection.reset
+ @open_transactions = 0
configure_connection
end
@@ -816,7 +814,7 @@ module ActiveRecord
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil)
- result = query(<<-SQL, name)
+ result = query(<<-SQL, 'SCHEMA')
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
FROM pg_class t
INNER JOIN pg_index d ON t.oid = d.indrelid
@@ -836,7 +834,7 @@ module ActiveRecord
inddef = row[3]
oid = row[4]
- columns = Hash[query(<<-SQL, "Columns for index #{row[0]} on #{table_name}")]
+ columns = Hash[query(<<-SQL, "SCHEMA")]
SELECT a.attnum, a.attname
FROM pg_attribute a
WHERE a.attrelid = #{oid}
@@ -848,7 +846,7 @@ module ActiveRecord
# add info on sort order for columns (only desc order is explicitly specified, asc is the default)
desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
-
+
column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders)
end.compact
end
@@ -863,7 +861,7 @@ module ActiveRecord
# Returns the current database name.
def current_database
- query('select current_database()')[0][0]
+ query('select current_database()', 'SCHEMA')[0][0]
end
# Returns the current schema name.
@@ -873,7 +871,7 @@ module ActiveRecord
# Returns the current database encoding format.
def encoding
- query(<<-end_sql)[0][0]
+ query(<<-end_sql, 'SCHEMA')[0][0]
SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
WHERE pg_database.datname LIKE '#{current_database}'
end_sql
@@ -936,7 +934,7 @@ module ActiveRecord
if pk && sequence
quoted_sequence = quote_table_name(sequence)
- select_value <<-end_sql, 'Reset sequence'
+ select_value <<-end_sql, 'SCHEMA'
SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
end_sql
end
@@ -946,7 +944,7 @@ module ActiveRecord
def pk_and_sequence_for(table) #:nodoc:
# First try looking for a sequence with a dependency on the
# given table's primary key.
- result = query(<<-end_sql, 'PK and serial sequence')[0]
+ result = query(<<-end_sql, 'SCHEMA')[0]
SELECT attr.attname, seq.relname
FROM pg_class seq,
pg_attribute attr,
@@ -964,16 +962,13 @@ module ActiveRecord
end_sql
if result.nil? or result.empty?
- # If that fails, try parsing the primary key's default value.
- # Support the 7.x and 8.0 nextval('foo'::text) as well as
- # the 8.1+ nextval('foo'::regclass).
- result = query(<<-end_sql, 'PK and custom sequence')[0]
+ result = query(<<-end_sql, 'SCHEMA')[0]
SELECT attr.attname,
CASE
- WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
- substr(split_part(def.adsrc, '''', 2),
- strpos(split_part(def.adsrc, '''', 2), '.')+1)
- ELSE split_part(def.adsrc, '''', 2)
+ WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
+ substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
+ strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
+ ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
END
FROM pg_class t
JOIN pg_attribute attr ON (t.oid = attrelid)
@@ -981,7 +976,7 @@ module ActiveRecord
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
WHERE t.oid = '#{quote_table_name(table)}'::regclass
AND cons.contype = 'p'
- AND def.adsrc ~* 'nextval'
+ AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval'
end_sql
end
@@ -998,7 +993,7 @@ module ActiveRecord
INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
WHERE cons.contype = 'p'
- AND dep.refobjid = '#{table}'::regclass
+ AND dep.refobjid = '#{quote_table_name(table)}'::regclass
end_sql
row && row.first
@@ -1085,7 +1080,7 @@ module ActiveRecord
end
when 'integer'
return 'integer' unless limit
-
+
case limit
when 1, 2; 'smallint'
when 3, 4; 'integer'
@@ -1281,7 +1276,8 @@ module ActiveRecord
# - ::regclass is a function that gives the id for a table name
def column_definitions(table_name) #:nodoc:
exec_query(<<-end_sql, 'SCHEMA').rows
- SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 00f74318c0..731f7e2049 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -359,12 +359,12 @@ module ActiveRecord
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil) #:nodoc:
- exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row|
+ exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", 'SCHEMA').map do |row|
IndexDefinition.new(
table_name,
row['name'],
row['unique'] != 0,
- exec_query("PRAGMA index_info('#{row['name']}')").map { |col|
+ exec_query("PRAGMA index_info('#{row['name']}')", 'SCHEMA').map { |col|
col['name']
})
end
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index 0ba54e6248..1ee465ab21 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -25,6 +25,10 @@ module ActiveRecord
self.name
end
+ if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
+ has_many_association = has_many_association.through_reflection
+ end
+
foreign_key = has_many_association.foreign_key.to_s
child_class = has_many_association.klass
belongs_to = child_class.reflect_on_all_associations(:belongs_to)
diff --git a/activerecord/lib/active_record/explain_subscriber.rb b/activerecord/lib/active_record/explain_subscriber.rb
index 1f8c4fc203..859c8edfc5 100644
--- a/activerecord/lib/active_record/explain_subscriber.rb
+++ b/activerecord/lib/active_record/explain_subscriber.rb
@@ -15,8 +15,9 @@ module ActiveRecord
# On the other hand, we want to monitor the performance of our real database
# queries, not the performance of the access to the query cache.
IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
+ EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)/i
def ignore_payload?(payload)
- payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name])
+ payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
end
ActiveSupport::Notifications.subscribe("sql.active_record", new)
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 83d9126ccb..987bb558e5 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -44,7 +44,7 @@ db_namespace = namespace :db do
def mysql_creation_options(config)
@charset = ENV['CHARSET'] || 'utf8'
@collation = ENV['COLLATION'] || 'utf8_unicode_ci'
- {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)}
+ {:charset => (config['encoding'] || @charset), :collation => (config['collation'] || @collation)}
end
def create_database(config)
@@ -96,8 +96,8 @@ db_namespace = namespace :db do
ActiveRecord::Base.establish_connection(config)
else
$stderr.puts sqlerr.error
- $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['charset'] || @charset}, collation: #{config['collation'] || @collation}"
- $stderr.puts "(if you set the charset manually, make sure you have a matching collation)" if config['charset']
+ $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['encoding'] || @charset}, collation: #{config['collation'] || @collation}"
+ $stderr.puts "(if you set the charset manually, make sure you have a matching collation)" if config['encoding']
end
end
when /postgresql/
@@ -369,7 +369,7 @@ db_namespace = namespace :db do
end
end
- task :load_if_ruby => [:environment, 'db:create'] do
+ task :load_if_ruby => ['db:create', :environment] do
db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby
end
end
@@ -412,7 +412,7 @@ db_namespace = namespace :db do
# desc "Recreate the databases from the structure.sql file"
task :load => [:environment, :load_config] do
- env = ENV['RAILS_ENV'] || 'test'
+ env = Rails.env
abcs = ActiveRecord::Base.configurations
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
@@ -445,7 +445,7 @@ db_namespace = namespace :db do
end
end
- task :load_if_sql => [:environment, 'db:create'] do
+ task :load_if_sql => ['db:create', :environment] do
db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql
end
end
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index 9c50baa647..d6b0265fb3 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -34,7 +34,7 @@ module ActiveRecord
if current_scope
current_scope.clone
else
- scope = relation.clone
+ scope = relation
scope.default_scoped = true
scope
end
@@ -48,7 +48,7 @@ module ActiveRecord
if current_scope
current_scope.scope_for_create
else
- scope = relation.clone
+ scope = relation
scope.default_scoped = true
scope.scope_for_create
end
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index 0c760e9850..ab25dc52f9 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -39,6 +39,7 @@ module ActiveRecord
def initialize_dup(other)
clear_timestamp_attributes
+ super
end
private
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 1b14df70e0..d0b51ef6a7 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -329,6 +329,7 @@ module ActiveRecord
@_start_transaction_state[:destroyed] = @destroyed
end
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
+ @_start_transaction_state[:frozen?] = @attributes.frozen?
end
# Clear the new record state and id of a record.
@@ -345,8 +346,8 @@ module ActiveRecord
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
if @_start_transaction_state[:level] < 1 || force
restore_state = remove_instance_variable(:@_start_transaction_state)
- was_frozen = @attributes.frozen?
- @attributes = @attributes.dup if was_frozen
+ was_frozen = restore_state[:frozen?]
+ @attributes = @attributes.dup if @attributes.frozen?
@new_record = restore_state[:new_record]
@destroyed = restore_state[:destroyed]
if restore_state.has_key?(:id)
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 21b97b3b39..28cdd1d588 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -10,5 +10,79 @@ module ActiveRecord
def test_encoding
assert_not_nil @connection.encoding
end
+
+ def test_reconnection_after_simulated_disconnection_with_verify
+ assert @connection.active?
+ original_connection_pid = @connection.query('select pg_backend_pid()')
+
+ # Fail with bad connection on next query attempt.
+ raw_connection = @connection.raw_connection
+ raw_connection_class = class << raw_connection ; self ; end
+ raw_connection_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def query_fake(*args)
+ if !( @called ||= false )
+ self.stubs(:status).returns(PGconn::CONNECTION_BAD)
+ @called = true
+ raise PGError
+ else
+ self.unstub(:status)
+ query_unfake(*args)
+ end
+ end
+
+ alias query_unfake query
+ alias query query_fake
+ CODE
+
+ begin
+ @connection.verify!
+ new_connection_pid = @connection.query('select pg_backend_pid()')
+ ensure
+ raw_connection_class.class_eval <<-CODE
+ alias query query_unfake
+ undef query_fake
+ CODE
+ end
+
+ assert_not_equal original_connection_pid, new_connection_pid, "Should have a new underlying connection pid"
+ end
+
+ # Must have with_manual_interventions set to true for this
+ # test to run.
+ # When prompted, restart the PostgreSQL server with the
+ # "-m fast" option or kill the individual connection assuming
+ # you know the incantation to do that.
+ # To restart PostgreSQL 9.1 on OS X, installed via MacPorts, ...
+ # sudo su postgres -c "pg_ctl restart -D /opt/local/var/db/postgresql91/defaultdb/ -m fast"
+ def test_reconnection_after_actual_disconnection_with_verify
+ return skip "with_manual_interventions is false in configuration" unless ARTest.config['with_manual_interventions']
+
+ original_connection_pid = @connection.query('select pg_backend_pid()')
+
+ # Sanity check.
+ assert @connection.active?
+
+ puts 'Kill the connection now (e.g. by restarting the PostgreSQL ' +
+ 'server with the "-m fast" option) and then press enter.'
+ $stdin.gets
+
+ @connection.verify!
+
+ assert @connection.active?
+
+ # If we get no exception here, then either we re-connected successfully, or
+ # we never actually got disconnected.
+ new_connection_pid = @connection.query('select pg_backend_pid()')
+
+ assert_not_equal original_connection_pid, new_connection_pid,
+ "umm -- looks like you didn't break the connection, because we're still " +
+ "successfully querying with the same connection pid."
+
+ # Repair all fixture connections so other tests won't break.
+ @fixture_connections.each do |c|
+ c.verify!
+ end
+ end
+
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 3eb73d3093..0de3786eb8 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -14,6 +14,10 @@ module ActiveRecord
assert_equal 'id', @connection.primary_key('ex')
end
+ def test_primary_key_works_tables_containing_capital_letters
+ assert_equal 'id', @connection.primary_key('CamelCase')
+ end
+
def test_non_standard_primary_key
@connection.exec_query('drop table if exists ex')
@connection.exec_query('create table ex(data character varying(255) primary key)')
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 1160d236c9..e7d1893ff3 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -63,6 +63,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal apple.id, citibank.firm_id
end
+ def test_id_assignment
+ apple = Firm.create("name" => "Apple")
+ citibank = Account.create("credit_limit" => 10)
+ assert_raise(NoMethodError) { citibank.firm_id = apple }
+ end
+
def test_natural_assignment_with_primary_key
apple = Firm.create("name" => "Apple")
citibank = Client.create("name" => "Primary key client")
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index bacab8d2f2..cad4cc9bf9 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -812,6 +812,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, developer.projects.count
end
+ def test_counting_should_not_fire_sql_if_parent_is_unsaved
+ assert_no_queries do
+ assert_equal 0, Developer.new.projects.count
+ end
+ end
+
unless current_adapter?(:PostgreSQLAdapter)
def test_count_with_finder_sql
assert_equal 3, projects(:active_record).developers_with_finder_sql.count
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index a51ce26117..ed4475770b 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -42,9 +42,12 @@ class HasManyAssociationsTestForCountWithCountSql < ActiveRecord::TestCase
end
end
-class HasManyAssociationsTestForCountDistinctWithFinderSql < ActiveRecord::TestCase
+class HasManyAssociationsTestForCountWithVariousFinderSqls < ActiveRecord::TestCase
class Invoice < ActiveRecord::Base
has_many :custom_line_items, :class_name => 'LineItem', :finder_sql => "SELECT DISTINCT line_items.amount from line_items"
+ has_many :custom_full_line_items, :class_name => 'LineItem', :finder_sql => "SELECT line_items.invoice_id, line_items.amount from line_items"
+ has_many :custom_star_line_items, :class_name => 'LineItem', :finder_sql => "SELECT * from line_items"
+ has_many :custom_qualified_star_line_items, :class_name => 'LineItem', :finder_sql => "SELECT line_items.* from line_items"
end
def test_should_count_distinct_results
@@ -55,6 +58,33 @@ class HasManyAssociationsTestForCountDistinctWithFinderSql < ActiveRecord::TestC
assert_equal 1, invoice.custom_line_items.count
end
+
+ def test_should_count_results_with_multiple_fields
+ invoice = Invoice.new
+ invoice.custom_full_line_items << LineItem.new(:amount => 0)
+ invoice.custom_full_line_items << LineItem.new(:amount => 0)
+ invoice.save!
+
+ assert_equal 2, invoice.custom_full_line_items.count
+ end
+
+ def test_should_count_results_with_star
+ invoice = Invoice.new
+ invoice.custom_star_line_items << LineItem.new(:amount => 0)
+ invoice.custom_star_line_items << LineItem.new(:amount => 0)
+ invoice.save!
+
+ assert_equal 2, invoice.custom_star_line_items.count
+ end
+
+ def test_should_count_results_with_qualified_star
+ invoice = Invoice.new
+ invoice.custom_qualified_star_line_items << LineItem.new(:amount => 0)
+ invoice.custom_qualified_star_line_items << LineItem.new(:amount => 0)
+ invoice.save!
+
+ assert_equal 2, invoice.custom_qualified_star_line_items.count
+ end
end
class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCase
@@ -271,6 +301,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal firm.limited_clients.length, firm.limited_clients.count
end
+ def test_counting_should_not_fire_sql_if_parent_is_unsaved
+ assert_no_queries do
+ assert_equal 0, Person.new.readers.count
+ end
+ end
+
def test_finding
assert_equal 2, Firm.find(:first, :order => "id").clients.length
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 4489c3e638..ffd7f4efa2 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -782,6 +782,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal 1, authors(:mary).categories.general.count
end
+ def test_counting_should_not_fire_sql_if_parent_is_unsaved
+ assert_no_queries do
+ assert_equal 0, Person.new.posts.count
+ end
+ end
+
def test_has_many_through_belongs_to_should_update_when_the_through_foreign_key_changes
post = posts(:eager_other)
@@ -845,6 +851,11 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_assign_array_to_new_record_builds_join_records
+ c = Category.new(:name => 'Fishing', :authors => [Author.first])
+ assert_equal 1, c.categorizations.size
+ end
+
def test_create_bang_should_raise_exception_when_join_record_has_errors
repair_validations(Categorization) do
Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index f4592f7d0e..8bd44e6444 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -236,6 +236,14 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_equal "2", categories(:sti_test).authors_with_select.first.post_id.to_s
end
+ def test_create_through_has_many_with_piggyback
+ category = categories(:sti_test)
+ ernie = category.authors_with_select.create(:name => 'Ernie')
+ assert_not_deprecated do
+ assert_equal ernie, category.authors_with_select.detect {|a| a.name == 'Ernie'}
+ end
+ end
+
def test_include_has_many_through
posts = Post.find(:all, :order => 'posts.id')
posts_with_authors = Post.find(:all, :include => :authors, :order => 'posts.id')
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index fc33438569..073e856e5e 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -919,6 +919,18 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
end
+ def test_attributes_on_dummy_time_with_invalid_time
+ # Oracle, and Sybase do not have a TIME datatype.
+ return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
+
+ attributes = {
+ "bonus_time" => "not a time"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_nil topic.bonus_time
+ end
+
def test_boolean
b_nil = Boolean.create({ "value" => nil })
nil_id = b_nil.id
diff --git a/activerecord/test/cases/column_test.rb b/activerecord/test/cases/column_test.rb
index ccc57cb876..4fcf8a33a4 100644
--- a/activerecord/test/cases/column_test.rb
+++ b/activerecord/test/cases/column_test.rb
@@ -24,6 +24,30 @@ module ActiveRecord
assert !column.type_cast('off')
assert !column.type_cast('OFF')
end
+
+ def test_type_cast_integer
+ column = Column.new("field", nil, "integer")
+ assert_equal 1, column.type_cast(1)
+ assert_equal 1, column.type_cast('1')
+ assert_equal 1, column.type_cast('1ignore')
+ assert_equal 0, column.type_cast('bad1')
+ assert_equal 0, column.type_cast('bad')
+ assert_equal 1, column.type_cast(1.7)
+ assert_nil column.type_cast(nil)
+ end
+
+ def test_type_cast_non_integer_to_integer
+ column = Column.new("field", nil, "integer")
+ assert_raises(NoMethodError) do
+ column.type_cast([])
+ end
+ assert_raises(NoMethodError) do
+ column.type_cast(true)
+ end
+ assert_raises(NoMethodError) do
+ column.type_cast(false)
+ end
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 5cecfa90e7..98759544b6 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -151,6 +151,28 @@ module ActiveRecord
def test_pool_sets_connection_visitor
assert @pool.connection.visitor.is_a?(Arel::Visitors::ToSql)
end
+
+ def test_timeout_spec_keys
+ # 'wait_timeout' is supported for backwards compat,
+ # 'checkout_timeout' is preferred to avoid conflicting
+ # with mysql2 adapters key of name 'wait_timeout' but
+ # different meaning.
+ config = ActiveRecord::Base.connection_pool.spec.config.merge(:wait_timeout => nil, :connection_timeout => nil)
+ method = ActiveRecord::Base.connection_pool.spec.adapter_method
+
+ pool = ConnectionPool.new ActiveRecord::Base::ConnectionSpecification.new(config.merge(:wait_timeout => 1), method)
+ assert_equal 1, pool.instance_variable_get(:@timeout)
+ pool.disconnect!
+
+ pool = ConnectionPool.new ActiveRecord::Base::ConnectionSpecification.new(config.merge(:checkout_timeout => 1), method)
+ assert_equal 1, pool.instance_variable_get(:@timeout)
+ pool.disconnect!
+
+ pool = ConnectionPool.new ActiveRecord::Base::ConnectionSpecification.new(config.merge(:wait_timeout => 6000, :checkout_timeout => 1), method)
+ assert_equal 1, pool.instance_variable_get(:@timeout)
+ pool.disconnect!
+ end
+
end
end
end
diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb
index ee443741ca..fc46a249c8 100644
--- a/activerecord/test/cases/counter_cache_test.rb
+++ b/activerecord/test/cases/counter_cache_test.rb
@@ -10,9 +10,12 @@ require 'models/dog'
require 'models/dog_lover'
require 'models/person'
require 'models/friendship'
+require 'models/subscriber'
+require 'models/subscription'
+require 'models/book'
class CounterCacheTest < ActiveRecord::TestCase
- fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers, :people, :friendships
+ fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers, :people, :friendships, :subscribers, :subscriptions, :books
class ::SpecialTopic < ::Topic
has_many :special_replies, :foreign_key => 'parent_id'
@@ -118,4 +121,14 @@ class CounterCacheTest < ActiveRecord::TestCase
Person.reset_counters(michael.id, :followers)
end
end
+
+ test "reset counter of has_many :through association" do
+ subscriber = subscribers('second')
+ Subscriber.reset_counters(subscriber.id, 'books')
+ Subscriber.increment_counter('books_count', subscriber.id)
+
+ assert_difference 'subscriber.reload.books_count', -1 do
+ Subscriber.reset_counters(subscriber.id, 'books')
+ end
+ end
end
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index b3a281d960..c63553ed51 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -110,3 +110,43 @@ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
end
end
end
+
+if current_adapter?(:PostgreSQLAdapter)
+ class DefaultsUsingMultipleSchemasAndDomainTest < ActiveSupport::TestCase
+ def setup
+ @connection = ActiveRecord::Base.connection
+
+ @old_search_path = @connection.schema_search_path
+ @connection.schema_search_path = "schema_1, pg_catalog"
+ @connection.create_table "defaults" do |t|
+ t.text "text_col", :default => "some value"
+ t.string "string_col", :default => "some value"
+ end
+ Default.reset_column_information
+ end
+
+ def test_text_defaults_in_new_schema_when_overriding_domain
+ assert_equal "some value", Default.new.text_col, "Default of text column was not correctly parse"
+ end
+
+ def test_string_defaults_in_new_schema_when_overriding_domain
+ assert_equal "some value", Default.new.string_col, "Default of string column was not correctly parse"
+ end
+
+ def test_bpchar_defaults_in_new_schema_when_overriding_domain
+ @connection.execute "ALTER TABLE defaults ADD bpchar_col bpchar DEFAULT 'some value'"
+ Default.reset_column_information
+ assert_equal "some value", Default.new.bpchar_col, "Default of bpchar column was not correctly parse"
+ end
+
+ def test_text_defaults_after_updating_column_default
+ @connection.execute "ALTER TABLE defaults ALTER COLUMN text_col SET DEFAULT 'some text'::schema_1.text"
+ assert_equal "some text", Default.new.text_col, "Default of text column was not correctly parse after updating default using '::text' since postgreSQL will add parens to the default in db"
+ end
+
+ def teardown
+ @connection.schema_search_path = @old_search_path
+ Default.reset_column_information
+ end
+ end
+end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 3bd2e909d7..4fc7e77786 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -521,6 +521,20 @@ class DirtyTest < ActiveRecord::TestCase
assert !pirate.previous_changes.key?('created_on')
end
+ if ActiveRecord::Base.connection.supports_migrations?
+ class Testings < ActiveRecord::Base; end
+ def test_field_named_field
+ ActiveRecord::Base.connection.create_table :testings do |t|
+ t.string :field
+ end
+ assert_nothing_raised do
+ Testings.new.attributes
+ end
+ ensure
+ ActiveRecord::Base.connection.drop_table :testings rescue nil
+ end
+ end
+
def test_setting_time_attributes_with_time_zone_field_to_same_time_should_not_be_marked_as_a_change
in_time_zone 'Paris' do
target = Class.new(ActiveRecord::Base)
@@ -530,7 +544,7 @@ class DirtyTest < ActiveRecord::TestCase
pirate = target.create(:created_on => created_on)
pirate.reload # Here mysql truncate the usec value to 0
-
+
pirate.created_on = created_on
assert !pirate.created_on_changed?
end
diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb
index 303f616c61..b2a3cb5733 100644
--- a/activerecord/test/cases/dup_test.rb
+++ b/activerecord/test/cases/dup_test.rb
@@ -98,5 +98,20 @@ module ActiveRecord
assert_not_nil new_topic.updated_at
assert_not_nil new_topic.created_at
end
+
+ def test_dup_validity_is_independent
+ Topic.validates_presence_of :title
+ topic = Topic.new("title" => "Litterature")
+ topic.valid?
+
+ duped = topic.dup
+ duped.title = nil
+ assert duped.invalid?
+
+ topic.title = nil
+ duped.title = 'Mathematics'
+ assert topic.invalid?
+ assert duped.valid?
+ end
end
end
diff --git a/activerecord/test/cases/explain_subscriber_test.rb b/activerecord/test/cases/explain_subscriber_test.rb
index e118add44c..7b852a625d 100644
--- a/activerecord/test/cases/explain_subscriber_test.rb
+++ b/activerecord/test/cases/explain_subscriber_test.rb
@@ -38,6 +38,13 @@ if ActiveRecord::Base.connection.supports_explain?
end
end
+ def test_collects_nothing_if_unexplained_sqls
+ with_queries([]) do |queries|
+ SUBSCRIBER.call(:name => 'SQL', :sql => 'SHOW max_identifier_length')
+ assert queries.empty?
+ end
+ end
+
def with_queries(queries)
Thread.current[:available_queries_for_explain] = queries
yield queries
@@ -45,4 +52,4 @@ if ActiveRecord::Base.connection.supports_explain?
Thread.current[:available_queries_for_explain] = nil
end
end
-end \ No newline at end of file
+end
diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb
index bc3dfb1078..e69243a537 100644
--- a/activerecord/test/cases/pooled_connections_test.rb
+++ b/activerecord/test/cases/pooled_connections_test.rb
@@ -17,7 +17,7 @@ class PooledConnectionsTest < ActiveRecord::TestCase
end
def checkout_connections
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :wait_timeout => 0.3}))
+ ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :checkout_timeout => 0.3}))
@connections = []
@timed_out = 0
@@ -42,7 +42,7 @@ class PooledConnectionsTest < ActiveRecord::TestCase
end
def checkout_checkin_connections(pool_size, threads)
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :wait_timeout => 0.5}))
+ ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :checkout_timeout => 0.5}))
@connection_count = 0
@timed_out = 0
threads.times do
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index cfcd11ca46..f33e765c59 100644
--- a/activerecord/test/cases/relation_scoping_test.rb
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -548,4 +548,8 @@ class DefaultScopingTest < ActiveRecord::TestCase
end
threads.each(&:join)
end
+
+ def test_default_scope_unscoped_is_not_cached
+ assert_not_equal DeveloperCalledDavid.unscoped.object_id, DeveloperCalledDavid.unscoped.object_id
+ end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 7639585649..b24df47efd 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -1093,6 +1093,10 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 'honda', FastCar.unscoped { FastCar.order_using_old_style.limit(1).first.name}
end
+ def test_unscoped_relation_clones
+ assert_not_equal CoolCar.unscoped.object_id, CoolCar.unscoped.object_id
+ end
+
def test_intersection_with_array
relation = Author.where(:name => "David")
rails_author = relation.first
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index c2fc239167..45b1c66544 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -14,6 +14,21 @@ class TransactionTest < ActiveRecord::TestCase
@first, @second = Topic.find(1, 2).sort_by { |t| t.id }
end
+ def test_raise_after_destroy
+ assert !@first.frozen?
+
+ assert_raises(RuntimeError) {
+ Topic.transaction do
+ @first.destroy
+ assert @first.frozen?
+ raise
+ end
+ }
+
+ assert @first.reload
+ assert !@first.frozen?
+ end
+
def test_successful
Topic.transaction do
@first.approved = true
diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb
index 88751a72f9..33d510499b 100644
--- a/activerecord/test/cases/xml_serialization_test.rb
+++ b/activerecord/test/cases/xml_serialization_test.rb
@@ -130,18 +130,18 @@ class NilXmlSerializationTest < ActiveRecord::TestCase
end
def test_should_serialize_string
- assert_match %r{<name nil="true"></name>}, @xml
+ assert_match %r{<name nil="true"/>}, @xml
end
def test_should_serialize_integer
- assert %r{<age (.*)></age>}.match(@xml)
+ assert %r{<age (.*)/>}.match(@xml)
attributes = $1
assert_match %r{nil="true"}, attributes
assert_match %r{type="integer"}, attributes
end
def test_should_serialize_binary
- assert %r{<avatar (.*)></avatar>}.match(@xml)
+ assert %r{<avatar (.*)/>}.match(@xml)
attributes = $1
assert_match %r{type="binary"}, attributes
assert_match %r{encoding="base64"}, attributes
@@ -149,21 +149,21 @@ class NilXmlSerializationTest < ActiveRecord::TestCase
end
def test_should_serialize_datetime
- assert %r{<created-at (.*)></created-at>}.match(@xml)
+ assert %r{<created-at (.*)/>}.match(@xml)
attributes = $1
assert_match %r{nil="true"}, attributes
assert_match %r{type="datetime"}, attributes
end
def test_should_serialize_boolean
- assert %r{<awesome (.*)></awesome>}.match(@xml)
+ assert %r{<awesome (.*)/>}.match(@xml)
attributes = $1
assert_match %r{type="boolean"}, attributes
assert_match %r{nil="true"}, attributes
end
def test_should_serialize_yaml
- assert_match %r{<preferences nil=\"true\"></preferences>}, @xml
+ assert_match %r{<preferences nil=\"true\"/>}, @xml
end
end
diff --git a/activerecord/test/config.example.yml b/activerecord/test/config.example.yml
index f450efd839..479b8c050d 100644
--- a/activerecord/test/config.example.yml
+++ b/activerecord/test/config.example.yml
@@ -1,5 +1,7 @@
default_connection: <%= defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3' %>
+with_manual_interventions: false
+
connections:
jdbcderby:
arunit: activerecord_unittest
diff --git a/activerecord/test/models/subscription.rb b/activerecord/test/models/subscription.rb
index 4bdb36ea46..bcac4738a3 100644
--- a/activerecord/test/models/subscription.rb
+++ b/activerecord/test/models/subscription.rb
@@ -1,4 +1,4 @@
class Subscription < ActiveRecord::Base
- belongs_to :subscriber
+ belongs_to :subscriber, :counter_cache => :books_count
belongs_to :book
end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 5cf9a207f3..34e24b5a2e 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -10,6 +10,8 @@ ActiveRecord::Schema.define do
execute "ALTER TABLE companies ALTER COLUMN id SET DEFAULT nextval('companies_nonstd_seq')"
execute 'DROP SEQUENCE IF EXISTS companies_id_seq'
+ execute "DROP SCHEMA IF EXISTS schema_1 CASCADE"
+
%w(accounts_id_seq developers_id_seq projects_id_seq topics_id_seq customers_id_seq orders_id_seq).each do |seq_name|
execute "SELECT setval('#{seq_name}', 100)"
end
@@ -35,7 +37,12 @@ ActiveRecord::Schema.define do
);
_SQL
- execute <<_SQL
+ execute "CREATE SCHEMA schema_1"
+ execute "CREATE DOMAIN schema_1.text AS text"
+ execute "CREATE DOMAIN schema_1.varchar AS varchar"
+ execute "CREATE DOMAIN schema_1.bpchar AS bpchar"
+
+ execute <<_SQL
CREATE TABLE geometrics (
id serial primary key,
a_point point,
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index b8150c50b9..8a3dfbb35a 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -596,6 +596,7 @@ ActiveRecord::Schema.define do
create_table :subscribers, :force => true, :id => false do |t|
t.string :nick, :null => false
t.string :name
+ t.column :books_count, :integer, :null => false, :default => 0
end
add_index :subscribers, :nick, :unique => true
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index f823118c8b..76a232665f 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,21 @@
+## Rails 3.2.9 (unreleased)
+
+* Add logger.push_tags and .pop_tags to complement logger.tagged:
+
+ class Job
+ def before
+ Rails.logger.push_tags :jobs, self.class.name
+ end
+
+ def after
+ Rails.logger.pop_tags 2
+ end
+ end
+
+ *Jeremy Kemper*
+
+* Add %:z and %::z format string support to ActiveSupport::TimeWithZone#strftime. [fixes #6962] *kennyj*
+
## Rails 3.2.8 (Aug 9, 2012) ##
* Fix ActiveSupport integration with Mocha > 0.12.1. *Mike Gunderloy*
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index 6af87e85e6..7e7f7ecfb2 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -15,16 +15,27 @@ module ActiveSupport
class TaggedLogging
def initialize(logger)
@logger = logger
- @tags = Hash.new { |h,k| h[k] = [] }
end
- def tagged(*new_tags)
- tags = current_tags
- new_tags = Array.wrap(new_tags).flatten.reject(&:blank?)
- tags.concat new_tags
- yield
+ def tagged(*tags)
+ new_tags = push_tags(*tags)
+ yield self
ensure
- new_tags.size.times { tags.pop }
+ pop_tags(new_tags.size)
+ end
+
+ def push_tags(*tags)
+ tags.flatten.reject(&:blank?).tap do |new_tags|
+ current_tags.concat new_tags
+ end
+ end
+
+ def pop_tags(size = 1)
+ current_tags.pop size
+ end
+
+ def clear_tags!
+ current_tags.clear
end
def silence(temporary_level = Logger::ERROR, &block)
@@ -46,7 +57,7 @@ module ActiveSupport
end
def flush
- @tags.delete(Thread.current)
+ clear_tags!
@logger.flush if @logger.respond_to?(:flush)
end
@@ -54,17 +65,16 @@ module ActiveSupport
@logger.send(method, *args)
end
- protected
-
- def tags_text
- tags = current_tags
- if tags.any?
- tags.collect { |tag| "[#{tag}]" }.join(" ") + " "
+ private
+ def tags_text
+ tags = current_tags
+ if tags.any?
+ tags.collect { |tag| "[#{tag}] " }.join
+ end
end
- end
- def current_tags
- @tags[Thread.current]
- end
+ def current_tags
+ Thread.current[:activesupport_tagged_logging_tags] ||= []
+ end
end
end
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index d3adf671a0..a3c378f057 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -177,7 +177,11 @@ module ActiveSupport
# Replaces <tt>%Z</tt> and <tt>%z</tt> directives with +zone+ and +formatted_offset+, respectively, before passing to
# Time#strftime, so that zone information is correct
def strftime(format)
- format = format.gsub('%Z', zone).gsub('%z', formatted_offset(false))
+ format = format.gsub('%Z', zone).
+ gsub('%z', formatted_offset(false)).
+ gsub('%:z', formatted_offset(true)).
+ gsub('%::z', formatted_offset(true) + ":00")
+
time.strftime(format)
end
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 56dfeb68cc..b5eb049ad4 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -614,7 +614,7 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal "<person>", xml.first(8)
assert xml.include?(%(<street>Paulina</street>))
assert xml.include?(%(<name>David</name>))
- assert xml.include?(%(<age nil="true"></age>))
+ assert xml.include?(%(<age nil="true"/>))
end
def test_one_level_with_skipping_types
@@ -622,7 +622,7 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal "<person>", xml.first(8)
assert xml.include?(%(<street>Paulina</street>))
assert xml.include?(%(<name>David</name>))
- assert xml.include?(%(<age nil="true"></age>))
+ assert xml.include?(%(<age nil="true"/>))
end
def test_one_level_with_yielding
diff --git a/activesupport/test/tagged_logging_test.rb b/activesupport/test/tagged_logging_test.rb
index c838c073e6..7253eb1222 100644
--- a/activesupport/test/tagged_logging_test.rb
+++ b/activesupport/test/tagged_logging_test.rb
@@ -29,6 +29,33 @@ class TaggedLoggingTest < ActiveSupport::TestCase
assert_equal "[BCX] [Jason] [New] Funky time\n", @output.string
end
+ test "tagged are flattened" do
+ @logger.tagged("BCX", %w(Jason New)) { @logger.info "Funky time" }
+ assert_equal "[BCX] [Jason] [New] Funky time\n", @output.string
+ end
+
+ test "push and pop tags directly" do
+ assert_equal %w(A B C), @logger.push_tags('A', ['B', ' ', ['C']])
+ @logger.info 'a'
+ assert_equal %w(C), @logger.pop_tags
+ @logger.info 'b'
+ assert_equal %w(B), @logger.pop_tags(1)
+ @logger.info 'c'
+ assert_equal [], @logger.clear_tags!
+ @logger.info 'd'
+ assert_equal "[A] [B] [C] a\n[A] [B] b\n[A] c\nd\n", @output.string
+ end
+
+ test "does not strip message content" do
+ @logger.info " Hello"
+ assert_equal " Hello\n", @output.string
+ end
+
+ test "provides access to the logger instance" do
+ @logger.tagged("BCX") { |logger| logger.info "Funky time" }
+ assert_equal "[BCX] Funky time\n", @output.string
+ end
+
test "tagged once with blank and nil" do
@logger.tagged(nil, "", "New") { @logger.info "Funky time" }
assert_equal "[New] Funky time\n", @output.string
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 3575175517..8ecfc1e47e 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -235,6 +235,14 @@ class TimeZoneTest < Test::Unit::TestCase
assert_equal "-0500", zone.formatted_offset(false)
end
+ def test_z_format_strings
+ zone = ActiveSupport::TimeZone['Tokyo']
+ twz = zone.now
+ assert_equal '+0900', twz.strftime('%z')
+ assert_equal '+09:00', twz.strftime('%:z')
+ assert_equal '+09:00:00', twz.strftime('%::z')
+ end
+
def test_formatted_offset_zero
zone = ActiveSupport::TimeZone['London']
assert_equal "+00:00", zone.formatted_offset
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index f41f91cf22..27f4fd6de7 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,7 @@
+## Rails 3.2.9 (unreleased)
+
+* Update supported ruby versions error message in ruby_version_check.rb *Lihan Li*
+
## Rails 3.2.8 (Aug 9, 2012) ##
* ERB scaffold generator use the `:data => { :confirm => "Text" }` syntax instead of `:confirm`.
diff --git a/railties/guides/source/3_1_release_notes.textile b/railties/guides/source/3_1_release_notes.textile
index ad4e2d8408..e2a1cbb99f 100644
--- a/railties/guides/source/3_1_release_notes.textile
+++ b/railties/guides/source/3_1_release_notes.textile
@@ -17,10 +17,132 @@ If you're upgrading an existing application, it's a great idea to have good test
h4. Rails 3.1 requires at least Ruby 1.8.7
+If your application is currently on any version of Rails older than 3.0.x, you should upgrade to Rails 3.0 before attempting an update to Rails 3.1.
+
+The following changes are meant for upgrading your application to Rails 3.1.3, the latest 3.1.x version of Rails.
+
+h4. Ruby
+
Rails 3.1 requires Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible. Rails 3.1 is also compatible with Ruby 1.9.2.
TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02 though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x jump on 1.9.2 for smooth sailing.
+h4. Gemfile
+
+Make the following changes to your +Gemfile+.
+
+<ruby>
+gem 'rails', '= 3.1.3'
+gem 'mysql2'
+
+# Needed for the new asset pipeline
+group :assets do
+ gem 'sass-rails', "~> 3.1.5"
+ gem 'coffee-rails', "~> 3.1.1"
+ gem 'uglifier', ">= 1.0.3"
+end
+
+# jQuery is the default JavaScript library in Rails 3.1
+gem 'jquery-rails'
+</ruby>
+
+h4. config/application.rb
+
+The asset pipeline requires the following additions:
+
+<ruby>
+config.assets.enabled = true
+config.assets.version = '1.0'
+</ruby>
+
+If your application is using an "/assets" route for a resource you may want change the prefix used for assets to avoid conflicts:
+
+<ruby>
+# Defaults to '/assets'
+config.assets.prefix = '/asset-files'
+</ruby>
+
+h4. config/environments/development.rb
+
+Remove the RJS setting <tt>config.action_view.debug_rjs = true</tt>.
+
+Add these settings if you enable the asset pipeline:
+
+<ruby>
+# Do not compress assets
+config.assets.compress = false
+
+# Expands the lines which load the assets
+config.assets.debug = true
+</ruby>
+
+h4. config/environments/production.rb
+
+Again, most of the changes below are for the asset pipeline. You can read more about these in the "Asset Pipeline":asset_pipeline.html guide.
+
+<ruby>
+# Compress JavaScripts and CSS
+config.assets.compress = true
+
+# Don't fallback to assets pipeline if a precompiled asset is missed
+config.assets.compile = false
+
+# Generate digests for assets URLs
+config.assets.digest = true
+
+# Defaults to Rails.root.join("public/assets")
+# config.assets.manifest = YOUR_PATH
+
+# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
+# config.assets.precompile += %w( search.js )
+
+# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
+# config.force_ssl = true
+</ruby>
+
+h4. config/environments/test.rb
+
+You can help test performance with these additions to your test environment:
+
+<ruby>
+# Configure static asset server for tests with Cache-Control for performance
+config.serve_static_assets = true
+config.static_cache_control = "public, max-age=3600"
+</ruby>
+
+h4. config/initializers/wrap_parameters.rb
+
+Add this file with the following contents, if you wish to wrap parameters into a nested hash. This is on by default in new applications.
+
+<ruby>
+# Be sure to restart your server when you modify this file.
+# This file contains settings for ActionController::ParamsWrapper which
+# is enabled by default.
+
+# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
+ActiveSupport.on_load(:action_controller) do
+ wrap_parameters :format => [:json]
+end
+
+# Disable root element in JSON by default.
+ActiveSupport.on_load(:active_record) do
+ self.include_root_in_json = false
+end
+</ruby>
+
+h4. config/initializers/session_store.rb
+
+You need to change your session key to something new, or remove all sessions:
+
+<ruby>
+# in config/initializers/session_store.rb
+AppName::Application.config.session_store :cookie_store, :key => 'SOMETHINGNEW'
+</ruby>
+
+or
+
+<tt>$ rake db:sessions:clear</tt>
+
h3. Creating a Rails 3.1 application
<shell>
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index 0bcd50a1c4..c32a23c50b 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -221,7 +221,7 @@ h3. Creating a New Rails Project
The best way to use this guide is to follow each step as it happens, no code or
step needed to make this example application has been left out, so you can
-literally follow along step by step. You can get the complete code "here":https://github.com/lifo/docrails/tree/master/railties/guides/code/getting_started.
+literally follow along step by step. You can get the complete code "here":https://github.com/rails/rails/tree/3-2-stable/railties/guides/code/getting_started.
By following along with this guide, you'll create a Rails project called <tt>blog</tt>, a
(very) simple weblog. Before you can start building the application, you need to
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index c0eb25a290..854ac2cbbc 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -232,6 +232,8 @@ module Rails
end
def default_middleware_stack
+ require 'action_controller/railtie'
+
ActionDispatch::MiddlewareStack.new.tap do |middleware|
if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache
require "action_dispatch/http/rack_cache"
diff --git a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
index 6a5d62803c..f33d56b564 100644
--- a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
+++ b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
@@ -1,13 +1,50 @@
module Rails
module Generators
class ResourceRouteGenerator < NamedBase
+
+ # Properly nests namespaces passed into a generator
+ #
+ # $ rails generate resource admin/users/products
+ #
+ # should give you
+ #
+ # namespace :admin do
+ # namespace :users
+ # resources :products
+ # end
+ # end
def add_resource_route
return if options[:actions].present?
- route_config = regular_class_path.collect{ |namespace| "namespace :#{namespace} do " }.join(" ")
- route_config << "resources :#{file_name.pluralize}"
- route_config << " end" * regular_class_path.size
- route route_config
+
+ # iterates over all namespaces and opens up blocks
+ regular_class_path.each_with_index do |namespace, index|
+ write("namespace :#{namespace} do", index + 1)
+ end
+
+ # inserts the primary resource
+ write("resources :#{file_name.pluralize}", route_length + 1)
+
+ # ends blocks
+ regular_class_path.each_index do |index|
+ write("end", route_length - index)
+ end
+
+ # route prepends two spaces onto the front of the string that is passed, this corrects that
+ route route_string[2..-1]
end
+
+ private
+ def route_string
+ @route_string ||= ""
+ end
+
+ def write(str, indent)
+ route_string << "#{" " * indent}#{str}\n"
+ end
+
+ def route_length
+ regular_class_path.length
+ end
end
end
end
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
index 89de10c83d..3f59bb8733 100644
--- a/railties/lib/rails/rack/logger.rb
+++ b/railties/lib/rails/rack/logger.rb
@@ -3,35 +3,48 @@ require 'active_support/core_ext/object/blank'
module Rails
module Rack
- # Log the request started and flush all loggers after it.
+ # Sets log tags, logs the request, calls the app, and flushes the logs.
class Logger < ActiveSupport::LogSubscriber
- def initialize(app, tags=nil)
- @app, @tags = app, tags.presence
+ def initialize(app, taggers = nil)
+ @app, @taggers = app, taggers || []
end
def call(env)
- if @tags
- Rails.logger.tagged(compute_tags(env)) { call_app(env) }
+ request = ActionDispatch::Request.new(env)
+
+ if Rails.logger.respond_to?(:tagged)
+ Rails.logger.tagged(compute_tags(request)) { call_app(request, env) }
else
- call_app(env)
+ call_app(request, env)
end
end
protected
- def call_app(env)
- request = ActionDispatch::Request.new(env)
- path = request.filtered_path
- Rails.logger.info "\n\nStarted #{request.request_method} \"#{path}\" for #{request.ip} at #{Time.now.to_default_s}"
+ def call_app(request, env)
+ # Put some space between requests in development logs.
+ if Rails.env.development?
+ Rails.logger.info ''
+ Rails.logger.info ''
+ end
+
+ Rails.logger.info started_request_message(request)
@app.call(env)
ensure
ActiveSupport::LogSubscriber.flush_all!
end
- def compute_tags(env)
- request = ActionDispatch::Request.new(env)
+ # Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
+ def started_request_message(request)
+ 'Started %s "%s" for %s at %s' % [
+ request.request_method,
+ request.filtered_path,
+ request.ip,
+ Time.now.to_default_s ]
+ end
- @tags.collect do |tag|
+ def compute_tags(request)
+ @taggers.collect do |tag|
case tag
when Proc
tag.call(request)
diff --git a/railties/lib/rails/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb
index 4d57c5973c..d6c7716c0d 100644
--- a/railties/lib/rails/ruby_version_check.rb
+++ b/railties/lib/rails/ruby_version_check.rb
@@ -2,7 +2,7 @@ if RUBY_VERSION < '1.8.7'
desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
abort <<-end_message
- Rails 3 requires Ruby 1.8.7 or 1.9.2.
+ Rails 3 requires Ruby 1.8.7 or >= 1.9.2.
You're running
#{desc}
@@ -14,7 +14,7 @@ elsif RUBY_VERSION > '1.9' and RUBY_VERSION < '1.9.2'
$stderr.puts <<-end_message
Rails 3 doesn't officially support Ruby 1.9.1 since recent stable
- releases have segfaulted the test suite. Please upgrade to Ruby 1.9.2.
+ releases have segfaulted the test suite. Please upgrade to Ruby 1.9.2 or later.
You're running
#{RUBY_DESCRIPTION}
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index 6e638ed85f..9e9702efb6 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -122,8 +122,23 @@ module ApplicationTests
app_file "app/assets/javascripts/something/index.js.erb", "alert();"
precompile!
-
assert File.exists?("#{app_path}/public/assets/something.js")
+
+ assets = YAML.load_file("#{app_path}/public/assets/manifest.yml")
+ assert_not_nil assets['something/index.js'], "Expected something/index.js among #{assets.keys.inspect}"
+ assert_not_nil assets['something.js'], "Expected something.js among #{assets.keys.inspect}"
+ end
+
+ test "precompile something/index.js for directory containing index file" do
+ add_to_config "config.assets.precompile = [ 'something/index.js' ]"
+ app_file "app/assets/javascripts/something/index.js.erb", "alert();"
+
+ precompile!
+ assert File.exists?("#{app_path}/public/assets/something/index.js")
+
+ assets = YAML.load_file("#{app_path}/public/assets/manifest.yml")
+ assert_not_nil assets['something/index.js'], "Expected something/index.js among #{assets.keys.inspect}"
+ assert_not_nil assets['something.js'], "Expected something.js among #{assets.keys.inspect}"
end
test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do
@@ -243,6 +258,7 @@ module ApplicationTests
test "assets raise AssetNotPrecompiledError when manifest file is present and requested file isn't precompiled if digest is disabled" do
app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'app' %>"
add_to_config "config.assets.compile = false"
+ add_to_config "config.assets.digest = false"
app_file "config/routes.rb", <<-RUBY
AppTemplate::Application.routes.draw do
@@ -250,14 +266,16 @@ module ApplicationTests
end
RUBY
- ENV["RAILS_ENV"] = "development"
+ ENV["RAILS_ENV"] = "production"
precompile!
# Create file after of precompile
app_file "app/assets/javascripts/app.js", "alert();"
require "#{app_path}/config/environment"
- class ::PostsController < ActionController::Base ; end
+ class ::PostsController < ActionController::Base
+ def show_detailed_exceptions?() true end
+ end
get '/posts'
assert_match(/AssetNotPrecompiledError/, last_response.body)
diff --git a/railties/test/application/rack/logger_test.rb b/railties/test/application/rack/logger_test.rb
index 387eb25525..c13f9a49e7 100644
--- a/railties/test/application/rack/logger_test.rb
+++ b/railties/test/application/rack/logger_test.rb
@@ -21,25 +21,25 @@ module ApplicationTests
end
def logs
- @logs ||= @logger.logged(:info)
+ @logs ||= @logger.logged(:info).join("\n")
end
test "logger logs proper HTTP verb and path" do
get "/blah"
wait
- assert_match(/^Started GET "\/blah"/, logs[0])
+ assert_match 'Started GET "/blah"', logs
end
test "logger logs HTTP verb override" do
- post "/", {:_method => 'put'}
+ post "/", :_method => 'put'
wait
- assert_match(/^Started PUT "\/"/, logs[0])
+ assert_match 'Started PUT "/"', logs
end
test "logger logs HEAD requests" do
- post "/", {:_method => 'head'}
+ post "/", :_method => 'head'
wait
- assert_match(/^Started HEAD "\/"/, logs[0])
+ assert_match 'Started HEAD "/"', logs
end
end
end
diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
index 88415b20e4..1cef42dafa 100644
--- a/railties/test/generators/namespaced_generators_test.rb
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -297,7 +297,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Route
assert_file "config/routes.rb" do |route|
- assert_match(/namespace :admin do resources :roles end$/, route)
+ assert_match(/^ namespace :admin do\n resources :roles\n end$/, route)
end
# Controller
@@ -339,7 +339,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Route
assert_file "config/routes.rb" do |route|
- assert_no_match(/namespace :admin do resources :roles end$/, route)
+ assert_no_match(/^ namespace :admin do\n resources :roles\n end$$/, route)
end
# Controller
@@ -357,4 +357,76 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Stylesheets (should not be removed)
assert_file "app/assets/stylesheets/scaffold.css"
end
+
+ def test_scaffold_with_nested_namespace_on_invoke
+ run_generator [ "admin/user/special/role", "name:string", "description:string" ]
+
+ # Model
+ assert_file "app/models/test_app/admin/user/special.rb", /module TestApp\n module Admin/
+ assert_file "app/models/test_app/admin/user/special/role.rb", /module TestApp\n class Admin::User::Special::Role < ActiveRecord::Base/
+ assert_file "test/unit/test_app/admin/user/special/role_test.rb", /module TestApp\n class Admin::User::Special::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/test_app/admin/user/special/roles.yml"
+ assert_migration "db/migrate/create_test_app_admin_user_special_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/^ namespace :admin do\n namespace :user do\n namespace :special do\n resources :roles\n end\n end\n end$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/test_app/admin/user/special/roles_controller.rb" do |content|
+ assert_match(/module TestApp\n class Admin::User::Special::RolesController < ApplicationController/, content)
+ end
+
+ assert_file "test/functional/test_app/admin/user/special/roles_controller_test.rb",
+ /module TestApp\n class Admin::User::Special::RolesControllerTest < ActionController::TestCase/
+
+ # Views
+ %w(
+ index
+ edit
+ new
+ show
+ _form
+ ).each { |view| assert_file "app/views/test_app/admin/user/special/roles/#{view}.html.erb" }
+ assert_no_file "app/views/layouts/admin/user/special/roles.html.erb"
+
+ # Helpers
+ assert_file "app/helpers/test_app/admin/user/special/roles_helper.rb"
+ assert_file "test/unit/helpers/test_app/admin/user/special/roles_helper_test.rb"
+
+ # Stylesheets
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
+
+ def test_scaffold_with_nested_namespace_on_revoke
+ run_generator [ "admin/user/special/role", "name:string", "description:string" ]
+ run_generator [ "admin/user/special/role" ], :behavior => :revoke
+
+ # Model
+ assert_file "app/models/test_app/admin/user/special.rb" # ( should not be remove )
+ assert_no_file "app/models/test_app/admin/user/special/role.rb"
+ assert_no_file "test/unit/test_app/admin/user/special/role_test.rb"
+ assert_no_file "test/fixtures/test_app/admin/user/special/roles.yml"
+ assert_no_migration "db/migrate/create_test_app_admin_user_special_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_no_match(/^ namespace :admin do\n namespace :user do\n namespace :special do\n resources :roles\n end\n end\n end$/, route)
+ end
+
+ # Controller
+ assert_no_file "app/controllers/test_app/admin/user/special/roles_controller.rb"
+ assert_no_file "test/functional/test_app/admin/user/special/roles_controller_test.rb"
+
+ # Views
+ assert_no_file "app/views/test_app/admin/user/special/roles"
+
+ # Helpers
+ assert_no_file "app/helpers/test_app/admin/user/special/roles_helper.rb"
+ assert_no_file "test/unit/helpers/test_app/admin/user/special/roles_helper_test.rb"
+
+ # Stylesheets (should not be removed)
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
end
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 2710ef7dfd..5891af505a 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -156,7 +156,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Route
assert_file "config/routes.rb" do |route|
- assert_match(/namespace :admin do resources :roles end$/, route)
+ assert_match(/^ namespace :admin do\n resources :roles\n end$/, route)
end
# Controller