aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--RAILS_VERSION2
-rw-r--r--actionmailer/CHANGELOG.md2
-rw-r--r--actionmailer/README.rdoc2
-rw-r--r--actionmailer/lib/action_mailer/base.rb3
-rw-r--r--actionmailer/lib/action_mailer/version.rb2
-rw-r--r--actionmailer/test/base_test.rb13
-rw-r--r--actionmailer/test/fixtures/anonymous/welcome.erb1
-rw-r--r--actionpack/CHANGELOG.md19
-rw-r--r--actionpack/README.rdoc2
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/action_controller/metal/head.rb3
-rw-r--r--actionpack/lib/action_controller/metal/params_wrapper.rb5
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb10
-rw-r--r--actionpack/lib/action_controller/test_case.rb25
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb9
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb21
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb1
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb31
-rw-r--r--actionpack/lib/action_pack/version.rb2
-rw-r--r--actionpack/lib/action_view/asset_paths.rb2
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb1
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb13
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/record_tag_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb17
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb2
-rw-r--r--actionpack/lib/sprockets/helpers/rails_helper.rb4
-rw-r--r--actionpack/lib/sprockets/static_compiler.rb3
-rw-r--r--actionpack/test/controller/assert_select_test.rb7
-rw-r--r--actionpack/test/controller/base_test.rb2
-rw-r--r--actionpack/test/controller/integration_test.rb80
-rw-r--r--actionpack/test/controller/params_wrapper_test.rb17
-rw-r--r--actionpack/test/controller/render_test.rb21
-rw-r--r--actionpack/test/controller/test_test.rb42
-rw-r--r--actionpack/test/dispatch/routing_test.rb135
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb52
-rw-r--r--actionpack/test/template/form_helper_test.rb52
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb39
-rw-r--r--actionpack/test/template/javascript_helper_test.rb36
-rw-r--r--actionpack/test/template/record_tag_helper_test.rb6
-rw-r--r--actionpack/test/template/render_test.rb55
-rw-r--r--actionpack/test/template/sprockets_helper_test.rb6
-rw-r--r--actionpack/test/template/text_helper_test.rb79
-rw-r--r--actionpack/test/template/url_helper_test.rb34
-rw-r--r--activemodel/CHANGELOG.md2
-rw-r--r--activemodel/README.rdoc4
-rw-r--r--activemodel/lib/active_model/validations.rb14
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb3
-rw-r--r--activemodel/lib/active_model/validations/confirmation.rb3
-rw-r--r--activemodel/lib/active_model/validations/exclusion.rb42
-rw-r--r--activemodel/lib/active_model/validations/format.rb55
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb36
-rw-r--r--activemodel/lib/active_model/validations/length.rb45
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb36
-rw-r--r--activemodel/lib/active_model/validations/presence.rb29
-rw-r--r--activemodel/lib/active_model/validations/with.rb21
-rw-r--r--activemodel/lib/active_model/version.rb2
-rw-r--r--activemodel/test/cases/attribute_methods_test.rb10
-rw-r--r--activemodel/test/cases/validations_test.rb15
-rw-r--r--activerecord/CHANGELOG.md11
-rw-r--r--activerecord/README.rdoc2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb16
-rw-r--r--activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb17
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb8
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb5
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb1
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb11
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb1
-rw-r--r--activerecord/lib/active_record/base.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb9
-rw-r--r--activerecord/lib/active_record/fixtures.rb36
-rw-r--r--activerecord/lib/active_record/identity_map.rb8
-rw-r--r--activerecord/lib/active_record/inheritance.rb25
-rw-r--r--activerecord/lib/active_record/railtie.rb8
-rw-r--r--activerecord/lib/active_record/relation.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb3
-rw-r--r--activerecord/lib/active_record/scoping/named.rb6
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb6
-rw-r--r--activerecord/lib/active_record/version.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/templates/migration.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb30
-rw-r--r--activerecord/test/cases/associations/eager_test.rb24
-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.rb24
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb2
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb14
-rw-r--r--activerecord/test/cases/autosave_association_test.rb8
-rw-r--r--activerecord/test/cases/base_test.rb20
-rw-r--r--activerecord/test/cases/explain_test.rb4
-rw-r--r--activerecord/test/cases/finder_test.rb1
-rw-r--r--activerecord/test/cases/identity_map_test.rb17
-rw-r--r--activerecord/test/cases/migration_test.rb14
-rw-r--r--activerecord/test/cases/relations_test.rb5
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb11
-rw-r--r--activerecord/test/models/comment.rb2
-rw-r--r--activerecord/test/schema/schema.rb1
-rw-r--r--activeresource/CHANGELOG.md2
-rw-r--r--activeresource/README.rdoc6
-rw-r--r--activeresource/lib/active_resource.rb1
-rw-r--r--activeresource/lib/active_resource/base.rb1
-rw-r--r--activeresource/lib/active_resource/version.rb2
-rw-r--r--activesupport/CHANGELOG.md68
-rw-r--r--activesupport/README.rdoc2
-rw-r--r--activesupport/lib/active_support/benchmarkable.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute_accessors.rb134
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/hash/deep_dup.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/hash/deep_merge.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/hash/keys.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/integer/multiple.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/object/duplicable.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/string/exclude.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb16
-rw-r--r--activesupport/lib/active_support/core_ext/time/zones.rb1
-rw-r--r--activesupport/lib/active_support/json/decoding.rb20
-rw-r--r--activesupport/lib/active_support/testing/performance/ruby.rb2
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb6
-rw-r--r--activesupport/lib/active_support/version.rb2
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/duplicable_test.rb14
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb14
-rw-r--r--railties/CHANGELOG.md5
-rw-r--r--railties/README.rdoc2
-rw-r--r--railties/guides/code/getting_started/Gemfile2
-rw-r--r--railties/guides/source/action_controller_overview.textile9
-rw-r--r--railties/guides/source/association_basics.textile4
-rw-r--r--railties/guides/source/caching_with_rails.textile18
-rw-r--r--railties/guides/source/configuring.textile2
-rw-r--r--railties/guides/source/contributing_to_ruby_on_rails.textile2
-rw-r--r--railties/guides/source/debugging_rails_applications.textile2
-rw-r--r--railties/guides/source/getting_started.textile25
-rw-r--r--railties/lib/rails/commands.rb10
-rw-r--r--railties/lib/rails/engine/commands.rb4
-rw-r--r--railties/lib/rails/generators.rb2
-rw-r--r--railties/lib/rails/generators/app_base.rb6
-rw-r--r--railties/lib/rails/generators/base.rb21
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/resource/resource_generator.rb8
-rw-r--r--railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb13
-rw-r--r--railties/lib/rails/generators/test_case.rb4
-rw-r--r--railties/lib/rails/rack/log_tailer.rb10
-rw-r--r--railties/lib/rails/tasks/documentation.rake2
-rw-r--r--railties/lib/rails/version.rb2
-rw-r--r--railties/railties.gemspec6
-rw-r--r--railties/test/application/assets_test.rb9
-rw-r--r--railties/test/application/initializers/active_record_test.rb62
-rw-r--r--railties/test/application/middleware/session_test.rb20
-rw-r--r--railties/test/generators/app_generator_test.rb16
-rw-r--r--railties/test/generators/migration_generator_test.rb4
-rw-r--r--railties/test/generators/shared_generator_tests.rb2
-rw-r--r--version.rb2
168 files changed, 1803 insertions, 482 deletions
diff --git a/Gemfile b/Gemfile
index 103dd1bd6c..ffddb50766 100644
--- a/Gemfile
+++ b/Gemfile
@@ -47,7 +47,7 @@ instance_eval File.read '.Gemfile' if File.exists? '.Gemfile'
platforms :mri do
group :test do
- gem 'ruby-prof' if RUBY_VERSION < '1.9.3'
+ gem 'ruby-prof', '~> 0.11.2'
end
end
diff --git a/RAILS_VERSION b/RAILS_VERSION
index 1bc5823a20..b347b11eac 100644
--- a/RAILS_VERSION
+++ b/RAILS_VERSION
@@ -1 +1 @@
-3.2.3.rc2
+3.2.3
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index a073018b42..bd75a27335 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,4 +1,4 @@
-## Rails 3.2.3 (unreleased) ##
+## Rails 3.2.3 (March 30, 2012) ##
* Upgrade mail version to 2.4.3 *ML*
diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc
index dc74b590f7..7667549608 100644
--- a/actionmailer/README.rdoc
+++ b/actionmailer/README.rdoc
@@ -143,7 +143,7 @@ The latest version of Action Mailer can be installed with RubyGems:
Source code can be downloaded as part of the Rails project on GitHub
-* https://github.com/rails/rails/tree/master/actionmailer
+* https://github.com/rails/rails/tree/3-2-stable/actionmailer
== License
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 8f2c567e3e..2a11cb6ca7 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -5,6 +5,7 @@ require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/proc'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/hash/except'
+require 'active_support/core_ext/module/anonymous'
require 'action_mailer/log_subscriber'
module ActionMailer #:nodoc:
@@ -375,7 +376,7 @@ module ActionMailer #:nodoc:
end
def mailer_name
- @mailer_name ||= name.underscore
+ @mailer_name ||= anonymous? ? "anonymous" : name.underscore
end
attr_writer :mailer_name
alias :controller_path :mailer_name
diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb
index 1beee98339..3149298635 100644
--- a/actionmailer/lib/action_mailer/version.rb
+++ b/actionmailer/lib/action_mailer/version.rb
@@ -3,7 +3,7 @@ module ActionMailer
MAJOR = 3
MINOR = 2
TINY = 3
- PRE = "rc2"
+ PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 9fdd0e1ced..d92fc01ef8 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -562,6 +562,19 @@ class BaseTest < ActiveSupport::TestCase
assert_equal ["notify"], FooMailer.action_methods
end
+ test "mailer can be anonymous" do
+ mailer = Class.new(ActionMailer::Base) do
+ def welcome
+ mail
+ end
+ end
+
+ assert_equal "anonymous", mailer.mailer_name
+
+ assert_equal "Welcome", mailer.welcome.subject
+ assert_equal "Anonymous mailer body", mailer.welcome.body.encoded.strip
+ end
+
protected
# Execute the block setting the given values and restoring old values after
diff --git a/actionmailer/test/fixtures/anonymous/welcome.erb b/actionmailer/test/fixtures/anonymous/welcome.erb
new file mode 100644
index 0000000000..8361da62c4
--- /dev/null
+++ b/actionmailer/test/fixtures/anonymous/welcome.erb
@@ -0,0 +1 @@
+Anonymous mailer body
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index b0e7890512..5bb5176083 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,9 +1,26 @@
-## Rails 3.2.3 (unreleased) ##
+## Rails 3.2.4 (unreleased) ##
+
+* Deprecate old APIs for highlight, excerpt and word_wrap *Jeremy Walker*
+
+* Deprecate `:disable_with` in favor of `'data-disable-with'` option for `button_to`, `button_tag` and `submit_tag` helpers.
+
+ *Carlos Galdino + Rafael Mendonça França*
+
+* Deprecate `:mouseover` option for `image_tag` helper. *Rafael Mendonça França*
+
+* Deprecate `button_to_function` and `link_to_function` helpers. *Rafael Mendonça França*
+
+
+## Rails 3.2.3 (March 30, 2012) ##
+
+* Allow to lazy load `default_form_builder` by passing a `String` instead of a constant. *Piotr Sarnacki*
* Fix #5632, render :inline set the proper rendered format. *Santiago Pastorino*
* Fix textarea rendering when using plugins like HAML. Such plugins encode the first newline character in the content. This issue was introduced in https://github.com/rails/rails/pull/5191 *James Coleman*
+* Remove the leading \n added by textarea on assert_select. *Santiago Pastorino*
+
* Add `config.action_view.embed_authenticity_token_in_remote_forms` (defaults to true) which allows to set if authenticity token will be included by default in remote forms. If you change it to false, you can still force authenticity token by passing `:authenticity_token => true` in form options *Piotr Sarnacki*
* Do not include the authenticity token in forms where remote: true as ajax forms use the meta-tag value *DHH*
diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc
index 185439f363..3539ce2d8f 100644
--- a/actionpack/README.rdoc
+++ b/actionpack/README.rdoc
@@ -322,7 +322,7 @@ The latest version of Action Pack can be installed with RubyGems:
Source code can be downloaded as part of the Rails project on GitHub
-* https://github.com/rails/rails/tree/master/actionpack
+* https://github.com/rails/rails/tree/3-2-stable/actionpack
== License
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index c7546aa66a..7f21b263e3 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.1')
- s.add_dependency('sprockets', '~> 2.1.2')
+ s.add_dependency('sprockets', '~> 2.1.3')
s.add_dependency('erubis', '~> 2.7.0')
s.add_development_dependency('tzinfo', '~> 0.3.29')
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index a618533d09..671053566d 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -20,6 +20,7 @@ module ActionController
options, status = status, nil if status.is_a?(Hash)
status ||= options.delete(:status) || :ok
location = options.delete(:location)
+ content_type = options.delete(:content_type)
options.each do |key, value|
headers[key.to_s.dasherize.split('-').each { |v| v[0] = v[0].chr.upcase }.join('-')] = value.to_s
@@ -27,7 +28,7 @@ module ActionController
self.status = status
self.location = url_for(location) if location
- self.content_type = Mime[formats.first] if formats
+ self.content_type = content_type || (Mime[formats.first] if formats)
self.response_body = " "
end
end
diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb
index 5c28a8074f..1ab436a205 100644
--- a/actionpack/lib/action_controller/metal/params_wrapper.rb
+++ b/actionpack/lib/action_controller/metal/params_wrapper.rb
@@ -167,8 +167,9 @@ module ActionController
unless options[:include] || options[:exclude]
model ||= _default_wrap_model
- if model.respond_to?(:accessible_attributes) && model.accessible_attributes.present?
- options[:include] = model.accessible_attributes.to_a
+ role = options.has_key?(:as) ? options[:as] : :default
+ if model.respond_to?(:accessible_attributes) && model.accessible_attributes(role).present?
+ options[:include] = model.accessible_attributes(role).to_a
elsif model.respond_to?(:attribute_names) && model.attribute_names.present?
options[:include] = model.attribute_names
end
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 3ffb7ef426..9abb86caf8 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -45,6 +45,16 @@ module ActionController
# integer, or a symbol representing the downcased, underscored and symbolized description.
# Note that the status code must be a 3xx HTTP code, or redirection will not occur.
#
+ # If you are using XHR requests other than GET or POST and redirecting after the
+ # request then some browsers will follow the redirect using the original request
+ # method. This may lead to undesirable behavior such as a double DELETE. To work
+ # around this you can return a <tt>303 See Other</tt> status code which will be
+ # followed using a GET request.
+ #
+ # Examples:
+ # redirect_to posts_url, :status => :see_other
+ # redirect_to :action => 'index', :status => 303
+ #
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for the commonly used flash names
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
#
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index d641fc6345..67c55a7f40 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -147,17 +147,23 @@ module ActionController
extra_keys = routes.extra_keys(parameters)
non_path_parameters = get? ? query_parameters : request_parameters
parameters.each do |key, value|
- if value.is_a? Fixnum
- value = value.to_s
- elsif value.is_a? Array
- value = Result.new(value.map { |v| v.is_a?(String) ? v.dup : v })
- elsif value.is_a? String
+ if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?))
+ value = value.map{ |v| v.duplicable? ? v.dup : v }
+ elsif value.is_a?(Hash) && (value.frozen? || value.any?{ |k,v| v.frozen? })
+ value = Hash[value.map{ |k,v| [k, v.duplicable? ? v.dup : v] }]
+ elsif value.frozen? && value.duplicable?
value = value.dup
end
if extra_keys.include?(key.to_sym)
non_path_parameters[key] = value
else
+ if value.is_a?(Array)
+ value = Result.new(value.map(&:to_param))
+ else
+ value = value.to_param
+ end
+
path_parameters[key.to_s] = value
end
end
@@ -426,7 +432,7 @@ module ActionController
def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
# Ensure that numbers and symbols passed as params are converted to
# proper params, as is the case when engaging rack.
- parameters = paramify_values(parameters)
+ parameters = paramify_values(parameters) if html_format?(parameters)
# Sanity check for required instance variables so we can give an
# understandable error message.
@@ -454,7 +460,6 @@ module ActionController
@request.session = ActionController::TestSession.new(session) if session
@request.session["flash"] = @request.flash.update(flash || {})
- @request.session["flash"].sweep
@controller.request = @request
build_request_uri(action, parameters)
@@ -507,6 +512,12 @@ module ActionController
@request.env["QUERY_STRING"] = query_string || ""
end
end
+
+ def html_format?(parameters)
+ return true unless parameters.is_a?(Hash)
+ format = Mime[parameters[:format]]
+ format.nil? || format.html?
+ end
end
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index ef5d207b26..cba98544bb 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -35,6 +35,10 @@ module ActionDispatch
@env["action_dispatch.request.path_parameters"] ||= {}
end
+ def reset_parameters #:nodoc:
+ @env.delete("action_dispatch.request.parameters")
+ end
+
private
# TODO: Validate that the characters are UTF-8. If they aren't,
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index bc5b163931..6f97c06b6b 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -4,7 +4,7 @@ module ActionDispatch
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
def flash
- @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new)
+ @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new).tap(&:sweep)
end
end
@@ -235,10 +235,6 @@ module ActionDispatch
end
def call(env)
- if (session = env['rack.session']) && (flash = session['flash'])
- flash.sweep
- end
-
@app.call(env)
ensure
session = env['rack.session'] || {}
@@ -255,7 +251,8 @@ module ActionDispatch
env[KEY] = new_hash
end
- if session.key?('flash') && session['flash'].empty?
+ if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
+ session.key?('flash') && session['flash'].empty?
session.delete('flash')
end
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 4c018968bf..82062c0cd5 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -34,6 +34,8 @@ module ActionDispatch
}
return true
+ ensure
+ req.reset_parameters
end
def call(env)
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index 330400e139..dc28389360 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -1,4 +1,6 @@
require 'action_dispatch/http/request'
+require 'active_support/core_ext/uri'
+require 'rack/utils'
module ActionDispatch
module Routing
@@ -46,8 +48,17 @@ module ActionDispatch
:params => request.query_parameters
}.merge options
+ if !params.empty? && url_options[:path].match(/%\{\w*\}/)
+ url_options[:path] = (url_options[:path] % escape_path(params))
+ end
+
ActionDispatch::Http::URL.url_for url_options
end
+
+ private
+ def escape_path(params)
+ Hash[params.map{ |k,v| [k, URI.parser.escape(v)] }]
+ end
end
module Redirection
@@ -93,7 +104,7 @@ module ActionDispatch
path = args.shift
block = lambda { |params, request|
- (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params)
+ (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % escape(params))
} if String === path
block = path if path.respond_to? :call
@@ -102,13 +113,19 @@ module ActionDispatch
if block && block.respond_to?(:arity) && block.arity < 2
msg = "redirect blocks with arity of #{block.arity} are deprecated. Your block must take 2 parameters: the environment, and a request object"
ActiveSupport::Deprecation.warn msg
- block = lambda { |params, _| block.call(params) }
+ deprecated_block = block
+ block = lambda { |params, _| deprecated_block.call(params) }
end
raise ArgumentError, "redirection argument not supported" unless block
Redirect.new status, block
end
+
+ private
+ def escape(params)
+ Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 8e7c997dc6..cde6cb20cc 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -178,7 +178,7 @@ module ActionDispatch
options = args.extract_options!
result = #{options.inspect}
- if args.any?
+ if args.size > 0
result[:_positional_args] = args
result[:_positional_keys] = #{route.segment_keys.inspect}
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index b4555f4f59..afbd869152 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -269,6 +269,7 @@ module ActionDispatch
end
end
text.strip! unless NO_STRIP.include?(match.name)
+ text.sub!(/\A\n/, '') if match.name == "textarea"
unless match_with.is_a?(Regexp) ? (text =~ match_with) : (text == match_with.to_s)
content_mismatch ||= build_message(message, "<?> expected but was\n<?>.", match_with, text)
true
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 0f1bb9f260..ad78854471 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -184,9 +184,16 @@ module ActionDispatch
reset!
end
- remove_method :default_url_options
- def default_url_options
- { :host => host, :protocol => https? ? "https" : "http" }
+ def url_options
+ @url_options ||= default_url_options.dup.tap do |url_options|
+ url_options.reverse_merge!(controller.url_options) if controller
+
+ if @app.respond_to?(:routes) && @app.routes.respond_to?(:default_url_options)
+ url_options.reverse_merge!(@app.routes.default_url_options)
+ end
+
+ url_options.reverse_merge!(:host => host, :protocol => https? ? "https" : "http")
+ end
end
# Resets the instance. This can be used to reset the state information
@@ -199,6 +206,7 @@ module ActionDispatch
@controller = @request = @response = nil
@_mock_session = nil
@request_count = 0
+ @url_options = nil
self.host = DEFAULT_HOST
self.remote_addr = "127.0.0.1"
@@ -293,6 +301,7 @@ module ActionDispatch
response = _mock_session.last_response
@response = ActionDispatch::TestResponse.new(response.status, response.headers, response.body)
@html_document = nil
+ @url_options = nil
@controller = session.last_request.env['action_controller.instance']
@@ -350,12 +359,14 @@ module ActionDispatch
end
end
- extend ActiveSupport::Concern
- include ActionDispatch::Routing::UrlFor
-
- def url_options
+ def default_url_options
reset! unless integration_session
+ integration_session.default_url_options
+ end
+
+ def default_url_options=(options)
integration_session.url_options
+ integration_session.default_url_options = options
end
def respond_to?(method, include_private = false)
@@ -459,6 +470,7 @@ module ActionDispatch
class IntegrationTest < ActiveSupport::TestCase
include Integration::Runner
include ActionController::TemplateAssertions
+ include ActionDispatch::Routing::UrlFor
@@app = nil
@@ -475,5 +487,10 @@ module ActionDispatch
def app
super || self.class.app
end
+
+ def url_options
+ reset! unless integration_session
+ integration_session.url_options
+ end
end
end
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index d101d283e7..4d1f19ed13 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -3,7 +3,7 @@ module ActionPack
MAJOR = 3
MINOR = 2
TINY = 3
- PRE = "rc2"
+ PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb
index aa5db0d7bc..f6115dbb1b 100644
--- a/actionpack/lib/action_view/asset_paths.rb
+++ b/actionpack/lib/action_view/asset_paths.rb
@@ -37,7 +37,7 @@ module ActionView
end
def is_uri?(path)
- path =~ %r{^[-a-z]+://|^cid:|^//}
+ path =~ %r{^[-a-z]+://|^(?:cid|data):|^//}
end
private
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 5dbba3c4a7..51d5d586bf 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -272,7 +272,7 @@ module ActionView
# The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
# plugin authors are encouraged to do so.
def image_path(source)
- asset_paths.compute_public_path(source, 'images')
+ source.present? ? asset_paths.compute_public_path(source, 'images') : ""
end
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
@@ -358,7 +358,7 @@ module ActionView
src = options[:src] = path_to_image(source)
- unless src =~ /^cid:/
+ unless src =~ /^(?:cid|data):/ || src.blank?
options[:alt] = options.fetch(:alt){ image_alt(src) }
end
@@ -367,6 +367,8 @@ module ActionView
end
if mouseover = options.delete(:mouseover)
+ ActiveSupport::Deprecation.warn ":mouseover option is deprecated and will be removed from Rails 4.0"
+
options[:onmouseover] = "this.src='#{path_to_image(mouseover)}'"
options[:onmouseout] = "this.src='#{src}'"
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
index dd4e9ae4cc..35f91cec18 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
@@ -1,5 +1,6 @@
require 'thread'
require 'active_support/core_ext/file'
+require 'active_support/core_ext/module/attribute_accessors'
module ActionView
module Helpers
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index edca950638..a3409ee3c7 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -9,6 +9,7 @@ require 'active_support/core_ext/module/method_names'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/string/inflections'
module ActionView
# = Action View Form Helpers
@@ -656,15 +657,16 @@ module ActionView
# 'Accept <a href="/terms">Terms</a>.'.html_safe
# end
def label(object_name, method, content_or_options = nil, options = nil, &block)
+ options ||= {}
+
content_is_options = content_or_options.is_a?(Hash)
if content_is_options || block_given?
- options = content_or_options if content_is_options
+ options.merge!(content_or_options) if content_is_options
text = nil
else
text = content_or_options
end
- options ||= {}
InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options, &block)
end
@@ -958,9 +960,14 @@ module ActionView
object_name = ActiveModel::Naming.param_key(object)
end
- builder = options[:builder] || ActionView::Base.default_form_builder
+ builder = options[:builder] || default_form_builder
builder.new(object_name, object, self, options, block)
end
+
+ def default_form_builder
+ builder = ActionView::Base.default_form_builder
+ builder.respond_to?(:constantize) ? builder.constantize : builder
+ end
end
class InstanceTag
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 066b98d4a2..3d3ae44eb4 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -417,6 +417,8 @@ module ActionView
options = options.stringify_keys
if disable_with = options.delete("disable_with")
+ ActiveSupport::Deprecation.warn ":disable_with option is deprecated and will be removed from Rails 4.0. Use 'data-disable-with' instead"
+
options["data-disable-with"] = disable_with
end
@@ -467,6 +469,8 @@ module ActionView
options = options.stringify_keys
if disable_with = options.delete("disable_with")
+ ActiveSupport::Deprecation.warn ":disable_with option is deprecated and will be removed from Rails 4.0. Use 'data-disable-with' instead"
+
options["data-disable-with"] = disable_with
end
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index 842f4c23a3..042ac4e9ee 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -82,6 +82,8 @@ module ActionView
# # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" />
#
def button_to_function(name, function=nil, html_options={})
+ ActiveSupport::Deprecation.warn("button_to_function is deprecated and will be removed from Rails 4.0")
+
onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
@@ -100,6 +102,8 @@ module ActionView
# # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a>
#
def link_to_function(name, function, html_options={})
+ ActiveSupport::Deprecation.warn("link_to_function is deprecated and will be removed from Rails 4.0")
+
onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
href = html_options[:href] || '#'
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index eda16903b0..b5e33dc0fc 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -427,10 +427,10 @@ module ActionView
# * *integers*: <tt>:unit</tt>, <tt>:ten</tt>, <tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>, <tt>:billion</tt>, <tt>:trillion</tt>, <tt>:quadrillion</tt>
# * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>, <tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>, <tt>:pico</tt>, <tt>:femto</tt>
# * <tt>:format</tt> - Sets the format of the output string (defaults to "%n %u"). The field types are:
- # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
- #
# %u The quantifier (ex.: 'thousand')
# %n The number
+ # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when the argument is invalid.
+ #
#
# ==== Examples
# number_to_human(123) # => "123"
diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb
index b351302d01..aae6389445 100644
--- a/actionpack/lib/action_view/helpers/record_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb
@@ -96,8 +96,8 @@ module ActionView
# for each record.
def content_tag_for_single_record(tag_name, record, prefix, options, &block)
options, prefix = prefix, nil if prefix.is_a?(Hash)
- options ||= {}
- options.merge!({ :class => "#{dom_class(record, prefix)} #{options[:class]}".strip, :id => dom_id(record, prefix) })
+ options = options ? options.dup : {}
+ options.merge!(:class => "#{dom_class(record, prefix)} #{options[:class]}".strip, :id => dom_id(record, prefix))
if block.arity == 0
content_tag(tag_name, capture(&block), options)
else
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 209360ee82..3490b1f34f 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -111,6 +111,9 @@ module ActionView
def highlight(text, phrases, *args)
options = args.extract_options!
unless args.empty?
+ ActiveSupport::Deprecation.warn "Calling highlight with a highlighter as an argument is deprecated. " \
+ "Please call with :highlighter => '#{args[0]}' instead.", caller
+
options[:highlighter] = args[0] || '<strong class="highlight">\1</strong>'
end
options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>')
@@ -156,6 +159,9 @@ module ActionView
options = args.extract_options!
unless args.empty?
+ ActiveSupport::Deprecation.warn "Calling excerpt with radius and omission as arguments is deprecated. " \
+ "Please call with :radius => #{args[0]}#{", :omission => '#{args[1]}'" if args[1]} instead.", caller
+
options[:radius] = args[0] || 100
options[:omission] = args[1] || "..."
end
@@ -217,6 +223,9 @@ module ActionView
def word_wrap(text, *args)
options = args.extract_options!
unless args.blank?
+ ActiveSupport::Deprecation.warn "Calling word_wrap with line_width as an argument is deprecated. " \
+ "Please call with :line_width => #{args[0]} instead.", caller
+
options[:line_width] = args[0] || 80
end
options.reverse_merge!(:line_width => 80)
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 1f1cd3cee3..6f05881d08 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -301,7 +301,7 @@ module ActionView
# # <div><input value="Create" type="submit" /></div>
# # </form>"
#
- #
+ #
# <%= button_to "Delete Image", { :action => "delete", :id => @image.id },
# :confirm => "Are you sure?", :method => :delete %>
# # => "<form method="post" action="/images/delete/1" class="button_to">
@@ -317,7 +317,7 @@ module ActionView
# # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
# # <div>
# # <input name='_method' value='delete' type='hidden' />
- # # <input value='Destroy' type='submit' disable_with='loading...' data-confirm='Are you sure?' />
+ # # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' />
# # </div>
# # </form>"
# #
@@ -333,9 +333,9 @@ module ActionView
form_method = method.to_s == 'get' ? 'get' : 'post'
form_options = html_options.delete('form') || {}
form_options[:class] ||= html_options.delete('form_class') || 'button_to'
-
+
remote = html_options.delete('remote')
-
+
request_token_tag = ''
if form_method == 'post' && protect_against_forgery?
request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
@@ -350,7 +350,7 @@ module ActionView
form_options.merge!(:method => form_method, :action => url)
form_options.merge!("data-remote" => "true") if remote
-
+
"#{tag(:form, form_options, true)}<div>#{method_tag}#{tag("input", html_options)}#{request_token_tag}</div></form>".html_safe
end
@@ -622,7 +622,12 @@ module ActionView
confirm = html_options.delete('confirm')
method = html_options.delete('method')
- html_options["data-disable-with"] = disable_with if disable_with
+ if disable_with
+ ActiveSupport::Deprecation.warn ":disable_with option is deprecated and will be removed from Rails 4.0. Use 'data-disable-with' instead"
+
+ html_options["data-disable-with"] = disable_with
+ end
+
html_options["data-confirm"] = confirm if confirm
add_method_to_attributes!(html_options, method) if method
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 25c41f042b..3093aca91e 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -407,7 +407,7 @@ module ActionView
end
def retrieve_variable(path)
- variable = @options[:as].try(:to_sym) || path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ variable = @options.fetch(:as) { path[%r'_?(\w+)(\.\w+)*$', 1] }.try(:to_sym)
variable_counter = :"#{variable}_counter" if @collection
[variable, variable_counter]
end
diff --git a/actionpack/lib/sprockets/helpers/rails_helper.rb b/actionpack/lib/sprockets/helpers/rails_helper.rb
index 3402343494..4e11221842 100644
--- a/actionpack/lib/sprockets/helpers/rails_helper.rb
+++ b/actionpack/lib/sprockets/helpers/rails_helper.rb
@@ -31,7 +31,7 @@ module Sprockets
else
super(source.to_s, { :src => path_to_asset(source, :ext => 'js', :body => body, :digest => digest) }.merge!(options))
end
- end.join("\n").html_safe
+ end.uniq.join("\n").html_safe
end
def stylesheet_link_tag(*sources)
@@ -48,7 +48,7 @@ module Sprockets
else
super(source.to_s, { :href => path_to_asset(source, :ext => 'css', :body => body, :protocol => :request, :digest => digest) }.merge!(options))
end
- end.join("\n").html_safe
+ end.uniq.join("\n").html_safe
end
def asset_path(source, options = {})
diff --git a/actionpack/lib/sprockets/static_compiler.rb b/actionpack/lib/sprockets/static_compiler.rb
index 32a9d66e6e..2e2db4b760 100644
--- a/actionpack/lib/sprockets/static_compiler.rb
+++ b/actionpack/lib/sprockets/static_compiler.rb
@@ -16,6 +16,9 @@ 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)
if asset = env.find_asset(logical_path)
manifest[logical_path] = write_asset(asset)
diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb
index 5eef8a32d7..97200cb6db 100644
--- a/actionpack/test/controller/assert_select_test.rb
+++ b/actionpack/test/controller/assert_select_test.rb
@@ -135,6 +135,13 @@ class AssertSelectTest < ActionController::TestCase
assert_raise(Assertion) { assert_select "pre", :html=>text }
end
+ def test_strip_textarea
+ render_html %Q{<textarea>\n\nfoo\n</textarea>}
+ assert_select "textarea", "\nfoo\n"
+ render_html %Q{<textarea>\nfoo</textarea>}
+ assert_select "textarea", "foo"
+ end
+
def test_counts
render_html %Q{<div id="1">foo</div><div id="2">foo</div>}
assert_nothing_raised { assert_select "div", 2 }
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index 145ef12b94..affa9a6add 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -68,7 +68,7 @@ class DefaultUrlOptionsController < ActionController::Base
render :inline => "<%= #{params[:route]} %>"
end
- def default_url_options(options = nil)
+ def default_url_options
{ :host => 'www.override.com', :action => 'new', :locale => 'en' }
end
end
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index f96d31eae8..fda263310b 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -539,3 +539,83 @@ class ApplicationIntegrationTest < ActionDispatch::IntegrationTest
assert_equal old_env, env
end
end
+
+class UrlOptionsIntegrationTest < ActionDispatch::IntegrationTest
+ class FooController < ActionController::Base
+ def index
+ render :text => "foo#index"
+ end
+
+ def show
+ render :text => "foo#show"
+ end
+
+ def edit
+ render :text => "foo#show"
+ end
+ end
+
+ class BarController < ActionController::Base
+ def default_url_options
+ { :host => "bar.com" }
+ end
+
+ def index
+ render :text => "foo#index"
+ end
+ end
+
+ def self.routes
+ @routes ||= ActionDispatch::Routing::RouteSet.new
+ end
+
+ def self.call(env)
+ routes.call(env)
+ end
+
+ def app
+ self.class
+ end
+
+ routes.draw do
+ default_url_options :host => "foo.com"
+
+ scope :module => "url_options_integration_test" do
+ get "/foo" => "foo#index", :as => :foos
+ get "/foo/:id" => "foo#show", :as => :foo
+ get "/foo/:id/edit" => "foo#edit", :as => :edit_foo
+ get "/bar" => "bar#index", :as => :bars
+ end
+ end
+
+ test "session uses default url options from routes" do
+ assert_equal "http://foo.com/foo", foos_url
+ end
+
+ test "current host overrides default url options from routes" do
+ get "/foo"
+ assert_response :success
+ assert_equal "http://www.example.com/foo", foos_url
+ end
+
+ test "controller can override default url options from request" do
+ get "/bar"
+ assert_response :success
+ assert_equal "http://bar.com/foo", foos_url
+ end
+
+ test "test can override default url options" do
+ default_url_options[:host] = "foobar.com"
+ assert_equal "http://foobar.com/foo", foos_url
+
+ get "/bar"
+ assert_response :success
+ assert_equal "http://foobar.com/foo", foos_url
+ end
+
+ test "current request path parameters are recalled" do
+ get "/foo/1"
+ assert_response :success
+ assert_equal "/foo/1/edit", url_for(:action => 'edit', :only_path => true)
+ end
+end
diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb
index c4d2614200..fa1608b9df 100644
--- a/actionpack/test/controller/params_wrapper_test.rb
+++ b/actionpack/test/controller/params_wrapper_test.rb
@@ -174,7 +174,7 @@ class ParamsWrapperTest < ActionController::TestCase
def test_accessible_wrapped_keys_from_matching_model
User.expects(:respond_to?).with(:accessible_attributes).returns(true)
- User.expects(:accessible_attributes).twice.returns(["username"])
+ User.expects(:accessible_attributes).with(:default).twice.returns(["username"])
with_default_wrapper_options do
@request.env['CONTENT_TYPE'] = 'application/json'
@@ -186,7 +186,7 @@ class ParamsWrapperTest < ActionController::TestCase
def test_accessible_wrapped_keys_from_specified_model
with_default_wrapper_options do
Person.expects(:respond_to?).with(:accessible_attributes).returns(true)
- Person.expects(:accessible_attributes).twice.returns(["username"])
+ Person.expects(:accessible_attributes).with(:default).twice.returns(["username"])
UsersController.wrap_parameters Person
@@ -195,6 +195,19 @@ class ParamsWrapperTest < ActionController::TestCase
assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'person' => { 'username' => 'sikachu' }})
end
end
+
+ def test_accessible_wrapped_keys_with_role_from_specified_model
+ with_default_wrapper_options do
+ Person.expects(:respond_to?).with(:accessible_attributes).returns(true)
+ Person.expects(:accessible_attributes).with(:admin).twice.returns(["username"])
+
+ UsersController.wrap_parameters Person, :as => :admin
+
+ @request.env['CONTENT_TYPE'] = 'application/json'
+ post :parse, { 'username' => 'sikachu', 'title' => 'Developer' }
+ assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'person' => { 'username' => 'sikachu' }})
+ end
+ end
def test_not_wrapping_abstract_model
User.expects(:respond_to?).with(:accessible_attributes).returns(false)
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 85abf3248d..b4ef22ecf7 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -495,6 +495,14 @@ class TestController < ActionController::Base
render :text => "hello world!"
end
+ def head_created
+ head :created
+ end
+
+ def head_created_with_application_json_content_type
+ head :created, :content_type => "application/json"
+ end
+
def head_with_location_header
head :location => "/foo"
end
@@ -1172,6 +1180,19 @@ class RenderTest < ActionController::TestCase
assert_equal "<html>\n <p>Hello</p>\n</html>\n", @response.body
end
+ def test_head_created
+ post :head_created
+ assert_blank @response.body
+ assert_response :created
+ end
+
+ def test_head_created_with_application_json_content_type
+ post :head_created_with_application_json_content_type
+ assert_blank @response.body
+ assert_equal "application/json", @response.content_type
+ assert_response :created
+ end
+
def test_head_with_location_header
get :head_with_location_header
assert_blank @response.body
diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb
index b64e275363..6a5843f9d7 100644
--- a/actionpack/test/controller/test_test.rb
+++ b/actionpack/test/controller/test_test.rb
@@ -46,6 +46,10 @@ class TestTest < ActionController::TestCase
render :text => request.fullpath
end
+ def test_format
+ render :text => request.format
+ end
+
def test_query_string
render :text => request.query_string
end
@@ -527,14 +531,34 @@ XML
)
end
+ def test_params_passing_with_fixnums_when_not_html_request
+ get :test_params, :format => 'json', :count => 999
+ parsed_params = eval(@response.body)
+ assert_equal(
+ {'controller' => 'test_test/test', 'action' => 'test_params',
+ 'format' => 'json', 'count' => 999 },
+ parsed_params
+ )
+ end
+
+ def test_params_passing_path_parameter_is_string_when_not_html_request
+ get :test_params, :format => 'json', :id => 1
+ parsed_params = eval(@response.body)
+ assert_equal(
+ {'controller' => 'test_test/test', 'action' => 'test_params',
+ 'format' => 'json', 'id' => '1' },
+ parsed_params
+ )
+ end
+
def test_params_passing_with_frozen_values
assert_nothing_raised do
- get :test_params, :frozen => 'icy'.freeze, :frozens => ['icy'.freeze].freeze
+ get :test_params, :frozen => 'icy'.freeze, :frozens => ['icy'.freeze].freeze, :deepfreeze => { :frozen => 'icy'.freeze }.freeze
end
parsed_params = eval(@response.body)
assert_equal(
{'controller' => 'test_test/test', 'action' => 'test_params',
- 'frozen' => 'icy', 'frozens' => ['icy']},
+ 'frozen' => 'icy', 'frozens' => ['icy'], 'deepfreeze' => { 'frozen' => 'icy' }},
parsed_params
)
end
@@ -635,6 +659,20 @@ XML
assert_equal "http://", @response.body
end
+ def test_request_format
+ get :test_format, :format => 'html'
+ assert_equal 'text/html', @response.body
+
+ get :test_format, :format => 'json'
+ assert_equal 'application/json', @response.body
+
+ get :test_format, :format => 'xml'
+ assert_equal 'application/xml', @response.body
+
+ get :test_format
+ assert_equal 'text/html', @response.body
+ end
+
def test_should_have_knowledge_of_client_side_cookie_state_even_if_they_are_not_set
cookies['foo'] = 'bar'
get :no_op
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index b96987410f..6a592acc35 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -63,8 +63,13 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
match 'secure', :to => redirect("/secure/login")
match 'mobile', :to => redirect(:subdomain => 'mobile')
+ match 'documentation', :to => redirect(:domain => 'example-documentation.com', :path => '')
+ match 'new_documentation', :to => redirect(:path => '/documentation/new')
match 'super_new_documentation', :to => redirect(:host => 'super-docs.com')
+ match 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}')
+ match 'stores/:name(*rest)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{rest}')
+
match 'youtube_favorites/:youtube_id/:name', :to => redirect(YoutubeFavoritesRedirector)
constraints(lambda { |req| true }) do
@@ -694,6 +699,21 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
def test_redirect_proc
with_test_routes do
get '/account/proc/person'
+
+ verify_redirect 'http://www.example.com/people'
+ end
+ end
+
+ def test_redirect_deprecated_proc
+ assert_deprecated do
+ self.class.stub_controllers do |routes|
+ routes.draw { match 'account/deprecated_proc/:name', :to => redirect {|params| "/#{params[:name].pluralize}" } }
+ end
+ end
+
+ with_test_routes do
+ get '/account/proc/person'
+
verify_redirect 'http://www.example.com/people'
end
end
@@ -712,6 +732,20 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_redirect_hash_with_domain_and_path
+ with_test_routes do
+ get '/documentation'
+ verify_redirect 'http://www.example-documentation.com'
+ end
+ end
+
+ def test_redirect_hash_with_path
+ with_test_routes do
+ get '/new_documentation'
+ verify_redirect 'http://www.example.com/documentation/new'
+ end
+ end
+
def test_redirect_hash_with_host
with_test_routes do
get '/super_new_documentation?section=top'
@@ -719,6 +753,20 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_redirect_hash_path_substitution
+ with_test_routes do
+ get '/stores/iernest'
+ verify_redirect 'http://stores.example.com/iernest'
+ end
+ end
+
+ def test_redirect_hash_path_substitution_with_catch_all
+ with_test_routes do
+ get '/stores/iernest/products'
+ verify_redirect 'http://stores.example.com/iernest/products'
+ end
+ end
+
def test_redirect_class
with_test_routes do
get '/youtube_favorites/oHg5SJYRHA0/rick-rolld'
@@ -2620,3 +2668,90 @@ class TestMultipleNestedController < ActionDispatch::IntegrationTest
end
+class TestRedirectInterpolation < ActionDispatch::IntegrationTest
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
+
+ get "/foo/:id" => redirect("/foo/bar/%{id}")
+ get "/bar/:id" => redirect(:path => "/foo/bar/%{id}")
+ get "/foo/bar/:id" => ok
+ end
+ end
+
+ def app; Routes end
+
+ test "redirect escapes interpolated parameters with redirect proc" do
+ get "/foo/1%3E"
+ verify_redirect "http://www.example.com/foo/bar/1%3E"
+ end
+
+ test "redirect escapes interpolated parameters with option proc" do
+ get "/bar/1%3E"
+ verify_redirect "http://www.example.com/foo/bar/1%3E"
+ end
+
+private
+ def verify_redirect(url, status=301)
+ assert_equal status, @response.status
+ assert_equal url, @response.headers['Location']
+ assert_equal expected_redirect_body(url), @response.body
+ end
+
+ def expected_redirect_body(url)
+ %(<html><body>You are being <a href="#{ERB::Util.h(url)}">redirected</a>.</body></html>)
+ end
+end
+
+class TestConstraintsAccessingParameters < ActionDispatch::IntegrationTest
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
+
+ get "/:foo" => ok, :constraints => lambda { |r| r.params[:foo] == 'foo' }
+ get "/:bar" => ok
+ end
+ end
+
+ def app; Routes end
+
+ test "parameters are reset between constraint checks" do
+ get "/bar"
+ assert_equal nil, @request.params[:foo]
+ assert_equal "bar", @request.params[:bar]
+ end
+end
+
+class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest
+ class CategoriesController < ActionController::Base
+ def show
+ render :text => "categories#show"
+ end
+ end
+
+ class ProductsController < ActionController::Base
+ def show
+ render :text => "products#show"
+ end
+ end
+
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ scope :module => "test_named_route_url_helpers" do
+ get "/categories/:id" => 'categories#show', :as => :category
+ get "/products/:id" => 'products#show', :as => :product
+ end
+ end
+ end
+
+ def app; Routes end
+
+ include Routes.url_helpers
+
+ test "url helpers do not ignore nil parameters" do
+ get "/categories/1"
+ assert_response :success
+ assert_raises(ActionController::RoutingError) { product_path(nil) }
+ end
+end
+
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index aa7304b3ed..e3f56ffea5 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -162,9 +162,9 @@ class AssetTagHelperTest < ActionView::TestCase
%(image_tag(".pdf.png")) => %(<img alt=".pdf" src="/images/.pdf.png" />),
%(image_tag("http://www.rubyonrails.com/images/rails.png")) => %(<img alt="Rails" src="http://www.rubyonrails.com/images/rails.png" />),
%(image_tag("//www.rubyonrails.com/images/rails.png")) => %(<img alt="Rails" src="//www.rubyonrails.com/images/rails.png" />),
- %(image_tag("mouse.png", :mouseover => "/images/mouse_over.png")) => %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />),
- %(image_tag("mouse.png", :mouseover => image_path("mouse_over.png"))) => %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />),
- %(image_tag("mouse.png", :alt => nil)) => %(<img src="/images/mouse.png" />)
+ %(image_tag("mouse.png", :alt => nil)) => %(<img src="/images/mouse.png" />),
+ %(image_tag("data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==", :alt => nil)) => %(<img src="data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" />),
+ %(image_tag("")) => %(<img src="" />)
}
FaviconLinkToTag = {
@@ -441,6 +441,22 @@ class AssetTagHelperTest < ActionView::TestCase
ImageLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_image_tag_with_mouseover
+ assert_deprecated ":mouseover option is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal(
+ %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />),
+ image_tag("mouse.png", :mouseover => "/images/mouse_over.png")
+ )
+ end
+
+ assert_deprecated ":mouseover option is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal(
+ %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />),
+ image_tag("mouse.png", :mouseover => image_path("mouse_over.png"))
+ )
+ end
+ end
+
def test_favicon_link_tag
FaviconLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -1095,8 +1111,14 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
assert_dom_equal(%(/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
assert_dom_equal(%(/collaboration/hieraki/images/xml.png), image_path("xml.png"))
- assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='/collaboration/hieraki/images/mouse.png'" src="/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
- assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='/collaboration/hieraki/images/mouse2.png'" src="/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
+
+ assert_deprecated ":mouseover option is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='/collaboration/hieraki/images/mouse.png'" src="/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
+ end
+
+ assert_deprecated ":mouseover option is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='/collaboration/hieraki/images/mouse2.png'" src="/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
+ end
end
def test_should_ignore_relative_root_path_on_complete_url
@@ -1109,8 +1131,14 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/images/xml.png), image_path("xml.png"))
- assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
- assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse2.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
+
+ assert_deprecated ":mouseover option is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
+ end
+
+ assert_deprecated ":mouseover option is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse2.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
+ end
end
def test_should_compute_proper_path_with_asset_host_and_default_protocol
@@ -1119,8 +1147,14 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/images/xml.png), image_path("xml.png"))
- assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
- assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse2.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
+
+ assert_deprecated ":mouseover option is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
+ end
+
+ assert_deprecated ":mouseover option is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse2.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
+ end
end
def test_should_ignore_asset_host_on_complete_url
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index f9940ead58..19af01e2c8 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -943,6 +943,41 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ def test_form_for_label_error_wrapping
+ form_for(@post) do |f|
+ concat f.label(:author_name, :class => 'label')
+ concat f.text_field(:author_name)
+ concat f.submit('Create post')
+ end
+
+ expected = whole_form("/posts/123", "edit_post_123" , "edit_post", :method => "put") do
+ "<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" +
+ "<div class='field_with_errors'><input name='post[author_name]' size='30' type='text' id='post_author_name' value='' /></div>" +
+ "<input name='commit' type='submit' value='Create post' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+
+ def test_form_for_label_error_wrapping_without_conventional_instance_variable
+ post = remove_instance_variable :@post
+
+ form_for(post) do |f|
+ concat f.label(:author_name, :class => 'label')
+ concat f.text_field(:author_name)
+ concat f.submit('Create post')
+ end
+
+ expected = whole_form("/posts/123", "edit_post_123" , "edit_post", :method => "put") do
+ "<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" +
+ "<div class='field_with_errors'><input name='post[author_name]' size='30' type='text' id='post_author_name' value='' /></div>" +
+ "<input name='commit' type='submit' value='Create post' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
def test_form_for_with_namespace
form_for(@post, :namespace => 'namespace') do |f|
concat f.text_field(:title)
@@ -1998,6 +2033,23 @@ class FormHelperTest < ActionView::TestCase
ActionView::Base.default_form_builder = old_default_form_builder
end
+ def test_lazy_loading_default_form_builder
+ old_default_form_builder, ActionView::Base.default_form_builder =
+ ActionView::Base.default_form_builder, "FormHelperTest::LabelledFormBuilder"
+
+ form_for(@post) do |f|
+ concat f.text_field(:title)
+ end
+
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'put') do
+ "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>"
+ end
+
+ assert_dom_equal expected, output_buffer
+ ensure
+ ActionView::Base.default_form_builder = old_default_form_builder
+ end
+
def test_fields_for_with_labelled_builder
output_buffer = fields_for(:post, @post, :builder => LabelledFormBuilder) do |f|
concat f.text_field(:title)
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index dcc6dd0411..d9f91e952b 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -367,17 +367,21 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_submit_tag
- assert_dom_equal(
- %(<input name='commit' data-disable-with="Saving..." onclick="alert('hello!')" type="submit" value="Save" />),
- submit_tag("Save", :disable_with => "Saving...", :onclick => "alert('hello!')")
- )
+ assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.0. Use 'data-disable-with' instead" do
+ assert_dom_equal(
+ %(<input name='commit' data-disable-with="Saving..." onclick="alert('hello!')" type="submit" value="Save" />),
+ submit_tag("Save", :disable_with => "Saving...", :onclick => "alert('hello!')")
+ )
+ end
end
def test_submit_tag_with_no_onclick_options
- assert_dom_equal(
- %(<input name='commit' data-disable-with="Saving..." type="submit" value="Save" />),
- submit_tag("Save", :disable_with => "Saving...")
- )
+ assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.0. Use 'data-disable-with' instead" do
+ assert_dom_equal(
+ %(<input name='commit' data-disable-with="Saving..." type="submit" value="Save" />),
+ submit_tag("Save", :disable_with => "Saving...")
+ )
+ end
end
def test_submit_tag_with_confirmation
@@ -388,10 +392,12 @@ class FormTagHelperTest < ActionView::TestCase
end
def test_submit_tag_with_confirmation_and_with_disable_with
- assert_dom_equal(
- %(<input name="commit" data-disable-with="Saving..." data-confirm="Are you sure?" type="submit" value="Save" />),
- submit_tag("Save", :disable_with => "Saving...", :confirm => "Are you sure?")
- )
+ assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.0. Use 'data-disable-with' instead" do
+ assert_dom_equal(
+ %(<input name="commit" data-disable-with="Saving..." data-confirm="Are you sure?" type="submit" value="Save" />),
+ submit_tag("Save", :disable_with => "Saving...", :confirm => "Are you sure?")
+ )
+ end
end
def test_button_tag
@@ -415,6 +421,15 @@ class FormTagHelperTest < ActionView::TestCase
)
end
+ def test_button_tag_with_disable_with
+ assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.0. Use 'data-disable-with' instead" do
+ assert_dom_equal(
+ %(<button name="button" data-disable-with="Saving..." type="submit">Save</button>),
+ button_tag("Save", :type => "submit", :disable_with => "Saving...")
+ )
+ end
+ end
+
def test_button_tag_with_reset_type
assert_dom_equal(
%(<button name="button" type="reset">Reset</button>),
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index 4b9c3c97b1..91968861fa 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -46,33 +46,45 @@ class JavaScriptHelperTest < ActionView::TestCase
end
def test_button_to_function
- assert_dom_equal %(<input type="button" onclick="alert('Hello world!');" value="Greeting" />),
- button_to_function("Greeting", "alert('Hello world!')")
+ assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal %(<input type="button" onclick="alert('Hello world!');" value="Greeting" />),
+ button_to_function("Greeting", "alert('Hello world!')")
+ end
end
def test_button_to_function_with_onclick
- assert_dom_equal "<input onclick=\"alert('Goodbye World :('); alert('Hello world!');\" type=\"button\" value=\"Greeting\" />",
- button_to_function("Greeting", "alert('Hello world!')", :onclick => "alert('Goodbye World :(')")
+ assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal "<input onclick=\"alert('Goodbye World :('); alert('Hello world!');\" type=\"button\" value=\"Greeting\" />",
+ button_to_function("Greeting", "alert('Hello world!')", :onclick => "alert('Goodbye World :(')")
+ end
end
def test_button_to_function_without_function
- assert_dom_equal "<input onclick=\";\" type=\"button\" value=\"Greeting\" />",
- button_to_function("Greeting")
+ assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal "<input onclick=\";\" type=\"button\" value=\"Greeting\" />",
+ button_to_function("Greeting")
+ end
end
def test_link_to_function
- assert_dom_equal %(<a href="#" onclick="alert('Hello world!'); return false;">Greeting</a>),
- link_to_function("Greeting", "alert('Hello world!')")
+ assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal %(<a href="#" onclick="alert('Hello world!'); return false;">Greeting</a>),
+ link_to_function("Greeting", "alert('Hello world!')")
+ end
end
def test_link_to_function_with_existing_onclick
- assert_dom_equal %(<a href="#" onclick="confirm('Sanity!'); alert('Hello world!'); return false;">Greeting</a>),
- link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')")
+ assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal %(<a href="#" onclick="confirm('Sanity!'); alert('Hello world!'); return false;">Greeting</a>),
+ link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')")
+ end
end
def test_function_with_href
- assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>),
- link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/')
+ assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.0" do
+ assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>),
+ link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/')
+ end
end
def test_javascript_tag
diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb
index 80d6f130ed..f33471ab28 100644
--- a/actionpack/test/template/record_tag_helper_test.rb
+++ b/actionpack/test/template/record_tag_helper_test.rb
@@ -100,4 +100,10 @@ class RecordTagHelperTest < ActionView::TestCase
result = content_tag_for(:li, [post_1, post_2]) { |post| concat post.body }
assert result.html_safe?
end
+
+ def test_content_tag_for_does_not_change_options_hash
+ options = { :class => "important" }
+ content_tag_for(:li, @post, options) { }
+ assert_equal({ :class => "important" }, options)
+ end
end
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 503bbb207e..bfeb6aee1a 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -21,9 +21,7 @@ module RenderTestCases
end
def test_render_without_options
- @view.render()
- flunk "Render did not raise ArgumentError"
- rescue ArgumentError => e
+ e = assert_raises(ArgumentError) { @view.render() }
assert_match "You invoked render but did not give any of :partial, :template, :inline, :file or :text option.", e.message
end
@@ -153,25 +151,26 @@ module RenderTestCases
end
def test_render_partial_with_invalid_name
- @view.render(:partial => "test/200")
- flunk "Render did not raise ArgumentError"
- rescue ArgumentError => e
+ e = assert_raises(ArgumentError) { @view.render(:partial => "test/200") }
assert_equal "The partial name (test/200) is not a valid Ruby identifier; " +
- "make sure your partial name starts with a letter or underscore, " +
- "and is followed by any combinations of letters, numbers, or underscores.", e.message
+ "make sure your partial name starts with a letter or underscore, " +
+ "and is followed by any combinations of letters, numbers, or underscores.", e.message
+ end
+
+ def test_render_partial_with_missing_filename
+ e = assert_raises(ArgumentError) { @view.render(:partial => "test/") }
+ assert_equal "The partial name (test/) is not a valid Ruby identifier; " +
+ "make sure your partial name starts with a letter or underscore, " +
+ "and is followed by any combinations of letters, numbers, or underscores.", e.message
end
def test_render_partial_with_incompatible_object
- @view.render(:partial => nil)
- flunk "Render did not raise ArgumentError"
- rescue ArgumentError => e
+ e = assert_raises(ArgumentError) { @view.render(:partial => nil) }
assert_equal "'#{nil.inspect}' is not an ActiveModel-compatible object that returns a valid partial path.", e.message
end
def test_render_partial_with_errors
- @view.render(:partial => "test/raise")
- flunk "Render did not raise Template::Error"
- rescue ActionView::Template::Error => e
+ e = assert_raises(ActionView::Template::Error) { @view.render(:partial => "test/raise") }
assert_match %r!method.*doesnt_exist!, e.message
assert_equal "", e.sub_template_message
assert_equal "1", e.line_number
@@ -180,9 +179,7 @@ module RenderTestCases
end
def test_render_sub_template_with_errors
- @view.render(:template => "test/sub_template_raise")
- flunk "Render did not raise Template::Error"
- rescue ActionView::Template::Error => e
+ e = assert_raises(ActionView::Template::Error) { @view.render(:template => "test/sub_template_raise") }
assert_match %r!method.*doesnt_exist!, e.message
assert_equal "Trace of template inclusion: #{File.expand_path("#{FIXTURE_LOAD_PATH}/test/sub_template_raise.html.erb")}", e.sub_template_message
assert_equal "1", e.line_number
@@ -190,9 +187,7 @@ module RenderTestCases
end
def test_render_file_with_errors
- @view.render(:file => File.expand_path("test/_raise", FIXTURE_LOAD_PATH))
- flunk "Render did not raise Template::Error"
- rescue ActionView::Template::Error => e
+ e = assert_raises(ActionView::Template::Error) { @view.render(:file => File.expand_path("test/_raise", FIXTURE_LOAD_PATH)) }
assert_match %r!method.*doesnt_exist!, e.message
assert_equal "", e.sub_template_message
assert_equal "1", e.line_number
@@ -300,7 +295,7 @@ module RenderTestCases
# TODO: The reason for this test is unclear, improve documentation
def test_render_missing_xml_partial_and_raise_missing_template
@view.formats = [:xml]
- assert_raise(ActionView::MissingTemplate) { @view.render(:partial => "test/layout_for_partial") }
+ assert_raises(ActionView::MissingTemplate) { @view.render(:partial => "test/layout_for_partial") }
ensure
@view.formats = nil
end
@@ -351,7 +346,7 @@ module RenderTestCases
def test_render_ignores_templates_with_malformed_template_handlers
ActiveSupport::Deprecation.silence do
%w(malformed malformed.erb malformed.html.erb malformed.en.html.erb).each do |name|
- assert_raise(ActionView::MissingTemplate) { @view.render(:file => "test/malformed/#{name}") }
+ assert_raises(ActionView::MissingTemplate) { @view.render(:file => "test/malformed/#{name}") }
end
end
end
@@ -477,23 +472,15 @@ class LazyViewRenderTest < ActiveSupport::TestCase
def test_render_utf8_template_with_incompatible_external_encoding
with_external_encoding Encoding::SHIFT_JIS do
- begin
- @view.render(:file => "test/utf8", :formats => [:html], :layouts => "layouts/yield")
- flunk 'Should have raised incompatible encoding error'
- rescue ActionView::Template::Error => error
- assert_match 'Your template was not saved as valid Shift_JIS', error.original_exception.message
- end
+ e = assert_raises(ActionView::Template::Error) { @view.render(:file => "test/utf8", :formats => [:html], :layouts => "layouts/yield") }
+ assert_match 'Your template was not saved as valid Shift_JIS', e.original_exception.message
end
end
def test_render_utf8_template_with_partial_with_incompatible_encoding
with_external_encoding Encoding::SHIFT_JIS do
- begin
- @view.render(:file => "test/utf8_magic_with_bare_partial", :formats => [:html], :layouts => "layouts/yield")
- flunk 'Should have raised incompatible encoding error'
- rescue ActionView::Template::Error => error
- assert_match 'Your template was not saved as valid Shift_JIS', error.original_exception.message
- end
+ e = assert_raises(ActionView::Template::Error) { @view.render(:file => "test/utf8_magic_with_bare_partial", :formats => [:html], :layouts => "layouts/yield") }
+ assert_match 'Your template was not saved as valid Shift_JIS', e.original_exception.message
end
end
diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb
index 1c591bdcc2..fc3c87a72e 100644
--- a/actionpack/test/template/sprockets_helper_test.rb
+++ b/actionpack/test/template/sprockets_helper_test.rb
@@ -254,6 +254,9 @@ class SprocketsHelperTest < ActionView::TestCase
assert_match %r{<script src="/assets/jquery.plugin.js" type="text/javascript"></script>},
javascript_include_tag('jquery.plugin', :digest => false)
+ assert_match %r{\A<script src="/assets/xmlhr-[0-9a-f]+.js" type="text/javascript"></script>\Z},
+ javascript_include_tag("xmlhr", "xmlhr")
+
@config.assets.compile = true
@config.assets.debug = true
assert_match %r{<script src="/javascripts/application.js" type="text/javascript"></script>},
@@ -301,6 +304,9 @@ class SprocketsHelperTest < ActionView::TestCase
assert_match %r{<link href="/assets/style-[0-9a-f]+.css\?body=1" media="screen" rel="stylesheet" type="text/css" />\n<link href="/assets/application-[0-9a-f]+.css\?body=1" media="screen" rel="stylesheet" type="text/css" />},
stylesheet_link_tag(:application, :debug => true)
+ assert_match %r{\A<link href="/assets/style-[0-9a-f]+.css" media="screen" rel="stylesheet" type="text/css" />\Z},
+ stylesheet_link_tag("style", "style")
+
@config.assets.compile = true
@config.assets.debug = true
assert_match %r{<link href="/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />},
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index a0afb77f05..9cfc18b540 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -120,7 +120,7 @@ class TextHelperTest < ActionView::TestCase
assert_equal(
"This is a <b>beautiful</b> morning, but also a <b>beautiful</b> day",
- highlight("This is a beautiful morning, but also a beautiful day", "beautiful", '<b>\1</b>')
+ highlight("This is a beautiful morning, but also a beautiful day", "beautiful", :highlighter => '<b>\1</b>')
)
assert_equal(
@@ -131,6 +131,12 @@ class TextHelperTest < ActionView::TestCase
assert_equal ' ', highlight(' ', 'blank text is returned verbatim')
end
+ def test_highlight_old_api_is_depcrecated
+ assert_deprecated("Calling highlight with a highlighter as an argument is deprecated. Please call with :highlighter => '<mark>\\1</mark>' instead.") do
+ highlight("This is a beautiful morning", "beautiful", '<mark>\1</mark>')
+ end
+ end
+
def test_highlight_should_sanitize_input
assert_equal(
"This is a <strong class=\"highlight\">beautiful</strong> morning",
@@ -163,14 +169,7 @@ class TextHelperTest < ActionView::TestCase
end
def test_highlight_with_multiple_phrases_in_one_pass
- assert_equal %(<em>wow</em> <em>em</em>), highlight('wow em', %w(wow em), '<em>\1</em>')
- end
-
- def test_highlight_with_options_hash
- assert_equal(
- "This is a <b>beautiful</b> morning, but also a <b>beautiful</b> day",
- highlight("This is a beautiful morning, but also a beautiful day", "beautiful", :highlighter => '<b>\1</b>')
- )
+ assert_equal %(<em>wow</em> <em>em</em>), highlight('wow em', %w(wow em), :highlighter => '<em>\1</em>')
end
def test_highlight_with_html
@@ -201,40 +200,48 @@ class TextHelperTest < ActionView::TestCase
end
def test_excerpt
- assert_equal("...is a beautiful morn...", excerpt("This is a beautiful morning", "beautiful", 5))
- assert_equal("This is a...", excerpt("This is a beautiful morning", "this", 5))
- assert_equal("...iful morning", excerpt("This is a beautiful morning", "morning", 5))
+ assert_equal("...is a beautiful morn...", excerpt("This is a beautiful morning", "beautiful", :radius => 5))
+ assert_equal("This is a...", excerpt("This is a beautiful morning", "this", :radius => 5))
+ assert_equal("...iful morning", excerpt("This is a beautiful morning", "morning", :radius => 5))
assert_nil excerpt("This is a beautiful morning", "day")
end
+ def test_excerpt_old_api_is_depcrecated
+ assert_deprecated("Calling excerpt with radius and omission as arguments is deprecated. Please call with :radius => 5 instead.") do
+ excerpt("This is a beautiful morning", "morning", 5)
+ end
+ assert_deprecated("Calling excerpt with radius and omission as arguments is deprecated. Please call with :radius => 5, :omission => 'mor' instead.") do
+ excerpt("This is a beautiful morning", "morning", 5, "mor")
+ end
+ end
+
def test_excerpt_should_not_be_html_safe
- assert !excerpt('This is a beautiful! morning', 'beautiful', 5).html_safe?
+ assert !excerpt('This is a beautiful! morning', 'beautiful', :radius => 5).html_safe?
end
def test_excerpt_in_borderline_cases
- assert_equal("", excerpt("", "", 0))
- assert_equal("a", excerpt("a", "a", 0))
- assert_equal("...b...", excerpt("abc", "b", 0))
- assert_equal("abc", excerpt("abc", "b", 1))
- assert_equal("abc...", excerpt("abcd", "b", 1))
- assert_equal("...abc", excerpt("zabc", "b", 1))
- assert_equal("...abc...", excerpt("zabcd", "b", 1))
- assert_equal("zabcd", excerpt("zabcd", "b", 2))
+ assert_equal("", excerpt("", "", :radius => 0))
+ assert_equal("a", excerpt("a", "a", :radius => 0))
+ assert_equal("...b...", excerpt("abc", "b", :radius => 0))
+ assert_equal("abc", excerpt("abc", "b", :radius => 1))
+ assert_equal("abc...", excerpt("abcd", "b", :radius => 1))
+ assert_equal("...abc", excerpt("zabc", "b", :radius => 1))
+ assert_equal("...abc...", excerpt("zabcd", "b", :radius => 1))
+ assert_equal("zabcd", excerpt("zabcd", "b", :radius => 2))
# excerpt strips the resulting string before ap-/prepending excerpt_string.
# whether this behavior is meaningful when excerpt_string is not to be
# appended is questionable.
- assert_equal("zabcd", excerpt(" zabcd ", "b", 4))
- assert_equal("...abc...", excerpt("z abc d", "b", 1))
+ assert_equal("zabcd", excerpt(" zabcd ", "b", :radius => 4))
+ assert_equal("...abc...", excerpt("z abc d", "b", :radius => 1))
end
def test_excerpt_with_regex
- assert_equal('...is a beautiful! mor...', excerpt('This is a beautiful! morning', 'beautiful', 5))
- assert_equal('...is a beautiful? mor...', excerpt('This is a beautiful? morning', 'beautiful', 5))
+ assert_equal('...is a beautiful! mor...', excerpt('This is a beautiful! morning', 'beautiful', :radius => 5))
+ assert_equal('...is a beautiful? mor...', excerpt('This is a beautiful? morning', 'beautiful', :radius => 5))
end
- def test_excerpt_with_options_hash
- assert_equal("...is a beautiful morn...", excerpt("This is a beautiful morning", "beautiful", :radius => 5))
+ def test_excerpt_with_omission
assert_equal("[...]is a beautiful morn[...]", excerpt("This is a beautiful morning", "beautiful", :omission => "[...]",:radius => 5))
assert_equal(
"This is the ultimate supercalifragilisticexpialidoceous very looooooooooooooooooong looooooooooooong beautiful morning with amazing sunshine and awesome tempera[...]",
@@ -246,30 +253,32 @@ class TextHelperTest < ActionView::TestCase
if RUBY_VERSION < '1.9'
def test_excerpt_with_utf8
with_kcode('u') do
- assert_equal("...\357\254\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped", 'could', 8))
+ assert_equal("...\357\254\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped", 'could', :radius => 8))
end
with_kcode('none') do
- assert_equal("...\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped", 'could', 8))
+ assert_equal("...\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped", 'could', :radius => 8))
end
end
else
def test_excerpt_with_utf8
- assert_equal("...\357\254\203ciency could not be...".force_encoding('UTF-8'), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding('UTF-8'), 'could', 8))
+ assert_equal("...\357\254\203ciency could not be...".force_encoding('UTF-8'), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding('UTF-8'), 'could', :radius => 8))
# .mb_chars always returns UTF-8, even in 1.9. This is not great, but it's how it works. Let's work this out.
# assert_equal("...\203ciency could not be...", excerpt("That's why e\357\254\203ciency could not be helped".force_encoding("BINARY"), 'could', 8))
end
end
def test_word_wrap
- assert_equal("my very very\nvery long\nstring", word_wrap("my very very very long string", 15))
+ assert_equal("my very very\nvery long\nstring", word_wrap("my very very very long string", :line_width => 15))
end
- def test_word_wrap_with_extra_newlines
- assert_equal("my very very\nvery long\nstring\n\nwith another\nline", word_wrap("my very very very long string\n\nwith another line", 15))
+ def test_word_wrap_old_api_is_depcrecated
+ assert_deprecated("Calling word_wrap with line_width as an argument is deprecated. Please call with :line_width => 15 instead.") do
+ word_wrap("my very very very long string", 15)
+ end
end
- def test_word_wrap_with_options_hash
- assert_equal("my very very\nvery long\nstring", word_wrap("my very very very long string", :line_width => 15))
+ def test_word_wrap_with_extra_newlines
+ assert_equal("my very very\nvery long\nstring\n\nwith another\nline", word_wrap("my very very very long string\n\nwith another line", :line_width => 15))
end
def test_pluralization
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index bc45fabf34..3a7cf9d8a3 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -82,16 +82,18 @@ class UrlHelperTest < ActiveSupport::TestCase
end
def test_button_to_with_javascript_disable_with
- assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :disable_with => "Greeting...")
- )
+ assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.0. Use 'data-disable-with' instead" do
+ assert_dom_equal(
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
+ button_to("Hello", "http://www.example.com", :disable_with => "Greeting...")
+ )
+ end
end
def test_button_to_with_remote_and_form_options
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"custom-class\" data-remote=\"true\" data-type=\"json\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com", :remote => true, :form => { :class => "custom-class", "data-type" => "json" } )
end
-
+
def test_button_to_with_remote_and_javascript_confirm
assert_dom_equal(
"<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
@@ -100,17 +102,21 @@ class UrlHelperTest < ActiveSupport::TestCase
end
def test_button_to_with_remote_and_javascript_disable_with
- assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => true, :disable_with => "Greeting...")
- )
+ assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.0. Use 'data-disable-with' instead" do
+ assert_dom_equal(
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
+ button_to("Hello", "http://www.example.com", :remote => true, :disable_with => "Greeting...")
+ )
+ end
end
def test_button_to_with_remote_and_javascript_confirm_and_javascript_disable_with
- assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-disable-with=\"Greeting...\" data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => true, :confirm => "Are you sure?", :disable_with => "Greeting...")
- )
+ assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.0. Use 'data-disable-with' instead" do
+ assert_dom_equal(
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-disable-with=\"Greeting...\" data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
+ button_to("Hello", "http://www.example.com", :remote => true, :confirm => "Are you sure?", :disable_with => "Greeting...")
+ )
+ end
end
def test_button_to_with_remote_false
@@ -548,7 +554,7 @@ class UrlHelperControllerTest < ActionController::TestCase
def test_named_route_should_show_host_and_path_using_controller_default_url_options
class << @controller
- def default_url_options(options = nil)
+ def default_url_options
{:host => 'testtwo.host'}
end
end
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index d3ea4c85dc..aeefc1723a 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,4 +1,4 @@
-## Rails 3.2.3 (unreleased) ##
+## Rails 3.2.3 (March 30, 2012) ##
* No changes.
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc
index 67701bc422..3ff1279aff 100644
--- a/activemodel/README.rdoc
+++ b/activemodel/README.rdoc
@@ -50,7 +50,7 @@ modules:
This generates +before_create+, +around_create+ and +after_create+
class methods that wrap your create method.
- {Learn more}[link:classes/ActiveModel/CallBacks.html]
+ {Learn more}[link:classes/ActiveModel/Callbacks.html]
* Tracking value changes
@@ -192,7 +192,7 @@ The latest version of Active Model can be installed with RubyGems:
Source code can be downloaded as part of the Rails project on GitHub
-* https://github.com/rails/rails/tree/master/activemodel
+* https://github.com/rails/rails/tree/3-2-stable/activemodel
== License
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 8ed392abca..18cd53e130 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -165,6 +165,20 @@ module ActiveModel
end
end
+ # Clean the +Errors+ object if instance is duped
+ def initialize_dup(other) # :nodoc:
+ @errors = nil
+ end
+
+ # Backport dup from 1.9 so that #initialize_dup gets called
+ unless Object.respond_to?(:initialize_dup)
+ def dup # :nodoc:
+ copy = super
+ copy.initialize_dup(self)
+ copy
+ end
+ end
+
# Returns the +Errors+ object that holds all information about attribute error messages.
def errors
@errors ||= Errors.new(self)
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index e628c6f306..38a32c479c 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -1,5 +1,4 @@
module ActiveModel
-
# == Active Model Acceptance Validator
module Validations
class AcceptanceValidator < EachValidator
@@ -59,7 +58,7 @@ module ActiveModel
# The method, proc or string should return or evaluate to a true or
# false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
def validates_acceptance_of(*attr_names)
validates_with AcceptanceValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index 6573a7d264..d1796dcbf2 100644
--- a/activemodel/lib/active_model/validations/confirmation.rb
+++ b/activemodel/lib/active_model/validations/confirmation.rb
@@ -1,5 +1,4 @@
module ActiveModel
-
# == Active Model Confirmation Validator
module Validations
class ConfirmationValidator < EachValidator
@@ -59,7 +58,7 @@ module ActiveModel
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
def validates_confirmation_of(*attr_names)
validates_with ConfirmationValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index 644cc814a7..725f810fa7 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -1,7 +1,6 @@
require 'active_support/core_ext/range'
module ActiveModel
-
# == Active Model Exclusion Validator
module Validations
class ExclusionValidator < EachValidator
@@ -24,43 +23,50 @@ module ActiveModel
private
- # In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible values in the
- # range for equality, so it may be slow for large ranges. The new <tt>Range#cover?</tt>
- # uses the previous logic of comparing a value with the range endpoints.
+ # In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible
+ # values in the range for equality, so it may be slow for large ranges. The new
+ # <tt>Range#cover?</tt> uses the previous logic of comparing a value with the
+ # range endpoints.
def inclusion_method(enumerable)
enumerable.is_a?(Range) ? :cover? : :include?
end
end
module HelperMethods
- # Validates that the value of the specified attribute is not in a particular enumerable object.
+ # Validates that the value of the specified attribute is not in a particular
+ # enumerable object.
#
# class Person < ActiveRecord::Base
# validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here"
# validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60"
# validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %{value} is not allowed"
- # validates_exclusion_of :password, :in => lambda { |p| [p.username, p.first_name] }, :message => "should not be the same as your username or first name"
+ # validates_exclusion_of :password, :in => lambda { |p| [p.username, p.first_name] },
+ # :message => "should not be the same as your username or first name"
# end
#
# Configuration options:
- # * <tt>:in</tt> - An enumerable object of items that the value shouldn't be part of.
- # This can be supplied as a proc or lambda which returns an enumerable. If the enumerable
- # is a range the test is performed with <tt>Range#cover?</tt>
+ # * <tt>:in</tt> - An enumerable object of items that the value shouldn't be
+ # part of. This can be supplied as a proc or lambda which returns an enumerable.
+ # If the enumerable is a range the test is performed with <tt>Range#cover?</tt>
# (backported in Active Support for 1.8), otherwise with <tt>include?</tt>.
# * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved").
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
- # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
+ # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute
+ # is +nil+ (default is +false+).
+ # * <tt>:allow_blank</tt> - If set to true, skips this validation if the
+ # attribute is blank (default is +false+).
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are <tt>:create</tt>
# and <tt>:update</tt>.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the
+ # validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
+ # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
+ # or string should return or evaluate to a true or false value.
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if
+ # the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
+ # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
+ # proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
def validates_exclusion_of(*attr_names)
validates_with ExclusionValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index d3faa8c6a6..225c95eb12 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -1,5 +1,4 @@
module ActiveModel
-
# == Active Model Format Validator
module Validations
class FormatValidator < EachValidator
@@ -42,50 +41,62 @@ module ActiveModel
end
module HelperMethods
- # Validates whether the value of the specified attribute is of the correct form, going by the regular expression provided.
- # You can require that the attribute matches the regular expression:
+ # Validates whether the value of the specified attribute is of the correct form,
+ # going by the regular expression provided. You can require that the attribute
+ # matches the regular expression:
#
# class Person < ActiveRecord::Base
# validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
# end
#
- # Alternatively, you can require that the specified attribute does _not_ match the regular expression:
+ # Alternatively, you can require that the specified attribute does _not_ match
+ # the regular expression:
#
# class Person < ActiveRecord::Base
# validates_format_of :email, :without => /NOSPAM/
# end
#
- # You can also provide a proc or lambda which will determine the regular expression that will be used to validate the attribute
+ # You can also provide a proc or lambda which will determine the regular
+ # expression that will be used to validate the attribute.
#
# class Person < ActiveRecord::Base
# # Admin can have number as a first letter in their screen name
- # validates_format_of :screen_name, :with => lambda{ |person| person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\Z/i : /\A[a-z][a-z0-9_\-]*\Z/i }
+ # validates_format_of :screen_name,
+ # :with => lambda{ |person| person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\Z/i : /\A[a-z][a-z0-9_\-]*\Z/i }
# end
#
- # Note: use <tt>\A</tt> and <tt>\Z</tt> to match the start and end of the string, <tt>^</tt> and <tt>$</tt> match the start/end of a line.
+ # Note: use <tt>\A</tt> and <tt>\Z</tt> to match the start and end of the string,
+ # <tt>^</tt> and <tt>$</tt> match the start/end of a line.
#
- # You must pass either <tt>:with</tt> or <tt>:without</tt> as an option. In addition, both must be a regular expression
- # or a proc or lambda, or else an exception will be raised.
+ # You must pass either <tt>:with</tt> or <tt>:without</tt> as an option. In
+ # addition, both must be a regular expression or a proc or lambda, or else an
+ # exception will be raised.
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
- # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
- # * <tt>:with</tt> - Regular expression that if the attribute matches will result in a successful validation.
- # This can be provided as a proc or lambda returning regular expression which will be called at runtime.
- # * <tt>:without</tt> - Regular expression that if the attribute does not match will result in a successful validation.
- # This can be provided as a proc or lambda returning regular expression which will be called at runtime.
+ # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute
+ # is +nil+ (default is +false+).
+ # * <tt>:allow_blank</tt> - If set to true, skips this validation if the
+ # attribute is blank (default is +false+).
+ # * <tt>:with</tt> - Regular expression that if the attribute matches will
+ # result in a successful validation. This can be provided as a proc or lambda
+ # returning regular expression which will be called at runtime.
+ # * <tt>:without</tt> - Regular expression that if the attribute does not match
+ # will result in a successful validation. This can be provided as a proc or
+ # lambda returning regular expression which will be called at runtime.
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are <tt>:create</tt>
# and <tt>:update</tt>.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the
+ # validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
+ # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
+ # or string should return or evaluate to a true or false value.
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if
+ # the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
+ # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
+ # proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
def validates_format_of(*attr_names)
validates_with FormatValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 147e2ecb69..568e877d95 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -1,7 +1,6 @@
require 'active_support/core_ext/range'
module ActiveModel
-
# == Active Model Inclusion Validator
module Validations
class InclusionValidator < EachValidator
@@ -24,16 +23,18 @@ module ActiveModel
private
- # In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible values in the
- # range for equality, so it may be slow for large ranges. The new <tt>Range#cover?</tt>
- # uses the previous logic of comparing a value with the range endpoints.
+ # In Ruby 1.9 <tt>Range#include?</tt> on non-numeric ranges checks all possible
+ # values in the range for equality, so it may be slow for large ranges. The new
+ # <tt>Range#cover?</tt> uses the previous logic of comparing a value with the
+ # range endpoints.
def inclusion_method(enumerable)
enumerable.is_a?(Range) ? :cover? : :include?
end
end
module HelperMethods
- # Validates whether the value of the specified attribute is available in a particular enumerable object.
+ # Validates whether the value of the specified attribute is available in a
+ # particular enumerable object.
#
# class Person < ActiveRecord::Base
# validates_inclusion_of :gender, :in => %w( m f )
@@ -47,20 +48,25 @@ module ActiveModel
# supplied as a proc or lambda which returns an enumerable. If the enumerable
# is a range the test is performed with <tt>Range#cover?</tt>
# (backported in Active Support for 1.8), otherwise with <tt>include?</tt>.
- # * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list").
- # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
- # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
+ # * <tt>:message</tt> - Specifies a custom error message (default is: "is not
+ # included in the list").
+ # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute
+ # is +nil+ (default is +false+).
+ # * <tt>:allow_blank</tt> - If set to true, skips this validation if the
+ # attribute is blank (default is +false+).
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are <tt>:create</tt>
# and <tt>:update</tt>.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
+ # the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
+ # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
+ # or string should return or evaluate to a true or false value.
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
+ # if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
+ # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
+ # proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
def validates_inclusion_of(*attr_names)
validates_with InclusionValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index a38de27b3c..b385a49c61 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -1,7 +1,6 @@
require "active_support/core_ext/string/encoding"
module ActiveModel
-
# == Active Model Length Validator
module Validations
class LengthValidator < EachValidator
@@ -68,8 +67,8 @@ module ActiveModel
end
module HelperMethods
-
- # Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
+ # Validates that the specified attribute matches the length restrictions supplied.
+ # Only one option can be used at a time:
#
# class Person < ActiveRecord::Base
# validates_length_of :first_name, :maximum => 30
@@ -79,35 +78,45 @@ module ActiveModel
# validates_length_of :user_name, :within => 6..20, :too_long => "pick a shorter name", :too_short => "pick a longer name"
# validates_length_of :zip_code, :minimum => 5, :too_short => "please enter at least 5 characters"
# validates_length_of :smurf_leader, :is => 4, :message => "papa is spelled with 4 characters... don't play me."
- # validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least 100 words.", :tokenizer => lambda { |str| str.scan(/\w+/) }
+ # validates_length_of :essay, :minimum => 100, :too_short => "Your essay must be at least 100 words.",
+ # :tokenizer => lambda { |str| str.scan(/\w+/) }
# end
#
# Configuration options:
# * <tt>:minimum</tt> - The minimum size of the attribute.
# * <tt>:maximum</tt> - The maximum size of the attribute.
# * <tt>:is</tt> - The exact size of the attribute.
- # * <tt>:within</tt> - A range specifying the minimum and maximum size of the attribute.
+ # * <tt>:within</tt> - A range specifying the minimum and maximum size of the
+ # attribute.
# * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
# * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
# * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
- # * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %{count} characters)").
- # * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %{count} characters)").
- # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be %{count} characters)").
- # * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
+ # * <tt>:too_long</tt> - The error message if the attribute goes over the
+ # maximum (default is: "is too long (maximum is %{count} characters)").
+ # * <tt>:too_short</tt> - The error message if the attribute goes under the
+ # minimum (default is: "is too short (min is %{count} characters)").
+ # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method
+ # and the attribute is the wrong size (default is: "is the wrong length
+ # should be %{count} characters)").
+ # * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>,
+ # <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate
+ # <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are <tt>:create</tt>
# and <tt>:update</tt>.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
+ # the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
+ # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
+ # or string should return or evaluate to a true or false value.
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
+ # if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
+ # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
- # * <tt>:tokenizer</tt> - Specifies how to split up the attribute string. (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to
- # count words as in above example.)
- # Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters.
+ # * <tt>:tokenizer</tt> - Specifies how to split up the attribute string.
+ # (e.g. <tt>:tokenizer => lambda {|str| str.scan(/\w+/)}</tt> to count words
+ # as in above example). Defaults to <tt>lambda{ |value| value.split(//) }</tt> which counts individual characters.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
def validates_length_of(*attr_names)
validates_with LengthValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 34d447a0fa..c73fa55ed3 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -1,5 +1,4 @@
module ActiveModel
-
# == Active Model Numericality Validator
module Validations
class NumericalityValidator < EachValidator
@@ -93,22 +92,30 @@ module ActiveModel
# validation contexts by default (+nil+), other options are <tt>:create</tt>
# and <tt>:update</tt>.
# * <tt>:only_integer</tt> - Specifies whether the value has to be an integer, e.g. an integral value (default is +false+).
- # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is +false+). Notice that for fixnum and float columns empty strings are converted to +nil+.
- # * <tt>:greater_than</tt> - Specifies the value must be greater than the supplied value.
- # * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be greater than or equal the supplied value.
+ # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is
+ # +false+). Notice that for fixnum and float columns empty strings are
+ # converted to +nil+.
+ # * <tt>:greater_than</tt> - Specifies the value must be greater than the
+ # supplied value.
+ # * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be greater
+ # than or equal the supplied value.
# * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied value.
- # * <tt>:less_than</tt> - Specifies the value must be less than the supplied value.
- # * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less than or equal the supplied value.
+ # * <tt>:less_than</tt> - Specifies the value must be less than the supplied
+ # value.
+ # * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less than
+ # or equal the supplied value.
# * <tt>:odd</tt> - Specifies the value must be an odd number.
# * <tt>:even</tt> - Specifies the value must be an even number.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
- # method, proc or string should return or evaluate to a true or false value.
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine
+ # if the validation should occur (e.g. <tt>:if => :allow_validation</tt>,
+ # or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method,
+ # proc or string should return or evaluate to a true or false value.
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
+ # if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
+ # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
+ # proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
#
# The following checks can also be supplied with a proc or a symbol which corresponds to a method:
# * <tt>:greater_than</tt>
@@ -117,11 +124,12 @@ module ActiveModel
# * <tt>:less_than</tt>
# * <tt>:less_than_or_equal_to</tt>
#
+ # For example:
+ #
# class Person < ActiveRecord::Base
# validates_numericality_of :width, :less_than => Proc.new { |person| person.height }
# validates_numericality_of :width, :greater_than => :minimum_weight
# end
- #
def validates_numericality_of(*attr_names)
validates_with NumericalityValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb
index 9a643a6f5c..efd1372a6b 100644
--- a/activemodel/lib/active_model/validations/presence.rb
+++ b/activemodel/lib/active_model/validations/presence.rb
@@ -1,7 +1,6 @@
require 'active_support/core_ext/object/blank'
module ActiveModel
-
# == Active Model Presence Validator
module Validations
class PresenceValidator < EachValidator
@@ -11,7 +10,8 @@ module ActiveModel
end
module HelperMethods
- # Validates that the specified attributes are not blank (as defined by Object#blank?). Happens by default on save. Example:
+ # Validates that the specified attributes are not blank (as defined by
+ # Object#blank?). Happens by default on save. Example:
#
# class Person < ActiveRecord::Base
# validates_presence_of :first_name
@@ -19,25 +19,28 @@ module ActiveModel
#
# The first_name attribute must be in the object and it cannot be blank.
#
- # If you want to validate the presence of a boolean field (where the real values are true and false),
- # you will want to use <tt>validates_inclusion_of :field_name, :in => [true, false]</tt>.
+ # If you want to validate the presence of a boolean field (where the real values
+ # are true and false), you will want to use <tt>validates_inclusion_of :field_name,
+ # :in => [true, false]</tt>.
#
- # This is due to the way Object#blank? handles boolean values: <tt>false.blank? # => true</tt>.
+ # This is due to the way Object#blank? handles boolean values:
+ # <tt>false.blank? # => true</tt>.
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are <tt>:create</tt>
# and <tt>:update</tt>.
- # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
- # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
- # The method, proc or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
- # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
- # The method, proc or string should return or evaluate to a true or false value.
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
+ # the validation should occur (e.g. <tt>:if => :allow_validation</tt>, or
+ # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
+ # or string should return or evaluate to a true or false value.
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
+ # if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
+ # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
+ # proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information
- #
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
def validates_presence_of(*attr_names)
validates_with PresenceValidator, _merge_attributes(attr_names)
end
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index 72b8562b93..4ad882b554 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -54,15 +54,14 @@ module ActiveModel
# (<tt>:create</tt> or <tt>:update</tt>
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
# if the validation should occur (e.g. <tt>:if => :allow_validation</tt>,
- # or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
- # The method, proc or string should return or evaluate to a true or false value.
- # * <tt>:unless</tt> - Specifies a method, proc or string to call to
- # determine if the validation should not occur
- # (e.g. <tt>:unless => :skip_validation</tt>, or
- # <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
- # The method, proc or string should return or evaluate to a true or false value.
+ # or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method,
+ # proc or string should return or evaluate to a true or false value.
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
+ # if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
+ # or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
+ # proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information
+ # See <tt>ActiveModel::Validation#validates!</tt> for more information.
# If you pass any additional configuration options, they will be passed
# to the class and available as <tt>options</tt>:
@@ -77,7 +76,6 @@ module ActiveModel
# options[:my_custom_key] # => "my custom value"
# end
# end
- #
def validates_with(*args, &block)
options = args.extract_options!
args.each do |klass|
@@ -128,12 +126,11 @@ module ActiveModel
# Standard configuration options (:on, :if and :unless), which are
# available on the class version of validates_with, should instead be
# placed on the <tt>validates</tt> method as these are applied and tested
- # in the callback
+ # in the callback.
#
# If you pass any additional configuration options, they will be passed
# to the class and available as <tt>options</tt>, please refer to the
- # class version of this method for more information
- #
+ # class version of this method for more information.
def validates_with(*args, &block)
options = args.extract_options!
args.each do |klass|
diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb
index c1b8d2eae3..29ae01c9a0 100644
--- a/activemodel/lib/active_model/version.rb
+++ b/activemodel/lib/active_model/version.rb
@@ -3,7 +3,7 @@ module ActiveModel
MAJOR = 3
MINOR = 2
TINY = 3
- PRE = "rc2"
+ PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb
index ff60100c08..e770728fd5 100644
--- a/activemodel/test/cases/attribute_methods_test.rb
+++ b/activemodel/test/cases/attribute_methods_test.rb
@@ -219,6 +219,12 @@ class AttributeMethodsTest < ActiveModel::TestCase
assert_raises(NoMethodError) { m.protected_method }
end
+ class ClassWithProtected
+ protected
+ def protected_method
+ end
+ end
+
test 'should not interfere with respond_to? if the attribute has a private/protected method' do
m = ModelWithAttributes2.new
m.attributes = { 'private_method' => '<3', 'protected_method' => 'O_o' }
@@ -226,9 +232,11 @@ class AttributeMethodsTest < ActiveModel::TestCase
assert !m.respond_to?(:private_method)
assert m.respond_to?(:private_method, true)
+ c = ClassWithProtected.new
+
# This is messed up, but it's how Ruby works at the moment. Apparently it will be changed
# in the future.
- assert m.respond_to?(:protected_method)
+ assert_equal c.respond_to?(:protected_method), m.respond_to?(:protected_method)
assert m.respond_to?(:protected_method, true)
end
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index 1941031e3c..56c9a5592c 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -339,4 +339,19 @@ class ValidationsTest < ActiveModel::TestCase
end
assert_equal "Title can't be blank", exception.message
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
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 16c525d211..8dab1f3db0 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,4 +1,13 @@
-## Rails 3.2.3 (unreleased) ##
+## Rails 3.2.4 (unreleased) ##
+
+* Perf fix: Don't load the records when doing assoc.delete_all.
+ GH #6289. *Jon Leighton*
+
+* Association preloading shouldn't be affected by the current scoping.
+ This could cause infinite recursion and potentially other problems.
+ See GH #5667. *Jon Leighton*
+
+## Rails 3.2.3 (March 30, 2012) ##
* Added find_or_create_by_{attribute}! dynamic method. *Andrew White*
diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc
index 70922ef864..aa8bf724b1 100644
--- a/activerecord/README.rdoc
+++ b/activerecord/README.rdoc
@@ -203,7 +203,7 @@ The latest version of Active Record can be installed with RubyGems:
Source code can be downloaded as part of the Rails project on GitHub
-* https://github.com/rails/rails/tree/master/activerecord
+* https://github.com/rails/rails/tree/3-2-stable/activerecord
== License
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 0f32ce7bd4..4c58094cfd 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -154,7 +154,7 @@ module ActiveRecord
#
# See delete for more info.
def delete_all
- delete(load_target).tap do
+ delete(:all).tap do
reset
loaded!
end
@@ -226,7 +226,17 @@ module ActiveRecord
# are actually removed from the database, that depends precisely on
# +delete_records+. They are in any case removed from the collection.
def delete(*records)
- delete_or_destroy(records, options[:dependent])
+ dependent = options[:dependent]
+
+ if records.first == :all
+ if loaded? || dependent == :destroy
+ delete_or_destroy(load_target, dependent)
+ else
+ delete_records(:all, dependent)
+ end
+ else
+ delete_or_destroy(records, dependent)
+ end
end
# Destroy +records+ and remove them from this association calling
@@ -481,6 +491,8 @@ module ActiveRecord
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
"new records could not be saved."
end
+
+ target
end
def concat_records(records)
diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
index a4cea99372..e5da0d585c 100644
--- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -44,13 +44,20 @@ module ActiveRecord
def delete_records(records, method)
if sql = options[:delete_sql]
+ records = load_target if records == :all
records.each { |record| owner.connection.delete(interpolate(sql, record)) }
else
- relation = join_table
- stmt = relation.where(relation[reflection.foreign_key].eq(owner.id).
- and(relation[reflection.association_foreign_key].in(records.map { |x| x.id }.compact))
- ).compile_delete
- owner.connection.delete stmt
+ relation = join_table
+ condition = relation[reflection.foreign_key].eq(owner.id)
+
+ unless records == :all
+ condition = condition.and(
+ relation[reflection.association_foreign_key].
+ in(records.map { |x| x.id }.compact)
+ )
+ end
+
+ owner.connection.delete(relation.where(condition).compile_delete)
end
end
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 059e6c77bc..e631579087 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -89,8 +89,12 @@ module ActiveRecord
records.each { |r| r.destroy }
update_counter(-records.length) unless inverse_updates_counter_cache?
else
- keys = records.map { |r| r[reflection.association_primary_key] }
- scope = scoped.where(reflection.association_primary_key => keys)
+ if records == :all
+ scope = scoped
+ else
+ keys = records.map { |r| r[reflection.association_primary_key] }
+ scope = scoped.where(reflection.association_primary_key => keys)
+ end
if method == :delete_all
update_counter(-scope.delete_all)
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..86c665b5fe 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -126,6 +126,10 @@ module ActiveRecord
def delete_records(records, method)
ensure_not_nested
+ # This is unoptimised; it will load all the target records
+ # even when we just want to delete everything.
+ records = load_target if records == :all
+
scope = through_association.scoped.where(construct_join_attributes(*records))
case method
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 779f8164cc..ab50e43ea0 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -77,7 +77,7 @@ module ActiveRecord
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
# Make several smaller queries if necessary or make one query if the adapter supports it
sliced = owner_keys.each_slice(model.connection.in_clause_length || owner_keys.size)
- records = sliced.map { |slice| records_for(slice) }.flatten
+ records = sliced.map { |slice| records_for(slice).to_a }.flatten
end
# Each record may have multiple owners, and vice-versa
@@ -93,7 +93,8 @@ module ActiveRecord
end
def build_scope
- scope = klass.scoped
+ scope = klass.unscoped
+ scope.default_scoped = true
scope = scope.where(process_conditions(options[:conditions]))
scope = scope.where(process_conditions(preload_options[:conditions]))
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 2a7a77777d..06b66b5195 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -64,6 +64,7 @@ module ActiveRecord
return if attribute_methods_generated?
superclass.define_attribute_methods unless self == base_class
super(column_names)
+ column_names.each { |name| define_external_attribute_method(name) }
@attribute_methods_generated = true
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 1548114580..a438de7c28 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -67,26 +67,27 @@ module ActiveRecord
# we first define with the __temp__ identifier, and then use alias method to
# rename it to what we want.
def define_method_attribute(attr_name)
- cast_code = attribute_cast_code(attr_name)
-
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__
- #{internal_attribute_access_code(attr_name, cast_code)}
+ #{internal_attribute_access_code(attr_name, attribute_cast_code(attr_name))}
end
alias_method '#{attr_name}', :__temp__
undef_method :__temp__
STR
+ end
+ private
+
+ def define_external_attribute_method(attr_name)
generated_external_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
def __temp__(v, attributes, attributes_cache, attr_name)
- #{external_attribute_access_code(attr_name, cast_code)}
+ #{external_attribute_access_code(attr_name, attribute_cast_code(attr_name))}
end
alias_method '#{attr_name}', :__temp__
undef_method :__temp__
STR
end
- private
def cacheable_column?(column)
attribute_types_cached_by_default.include?(column.type)
end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index 17cf34cdf6..c9b2edbd8c 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -43,6 +43,7 @@ module ActiveRecord
end
time = time.in_time_zone rescue nil if time
write_attribute(:#{attr_name}, original_time)
+ #{attr_name}_will_change!
@attributes_cache["#{attr_name}"] = time
end
EOV
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 9746efc7d1..8c8717b759 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
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 3af285c7c8..f44cba3978 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -92,14 +92,18 @@ module ActiveRecord
# #connection can be called any number of times; the connection is
# held in a hash keyed by the thread id.
def connection
- @reserved_connections[current_connection_id] ||= checkout
+ synchronize do
+ @reserved_connections[current_connection_id] ||= checkout
+ end
end
# Is there an open connection that is being used for the current thread?
def active_connection?
- @reserved_connections.fetch(current_connection_id) {
- return false
- }.in_use?
+ synchronize do
+ @reserved_connections.fetch(current_connection_id) {
+ return false
+ }.in_use?
+ end
end
# Signal that the thread is finished with the current connection.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index a0e2900120..08c3917d7e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -269,7 +269,15 @@ module ActiveRecord
# remove_column(:suppliers, :qualification)
# remove_columns(:suppliers, :qualification, :experience)
def remove_column(table_name, *column_names)
- columns_for_remove(table_name, *column_names).each {|column_name| execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}" }
+ if column_names.flatten!
+ message = 'Passing array to remove_columns is deprecated, please use ' +
+ 'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`'
+ ActiveSupport::Deprecation.warn message, caller
+ end
+
+ columns_for_remove(table_name, *column_names).each do |column_name|
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{column_name}"
+ end
end
alias :remove_columns :remove_column
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index d3f5600119..2658c444d1 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -525,7 +525,7 @@ module ActiveRecord
def pk_and_sequence_for(table)
execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
create_table = each_hash(result).first[:"Create Table"]
- if create_table.to_s =~ /PRIMARY KEY\s+\((.+)\)/
+ if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
keys = $1.split(",").map { |key| key.gsub(/[`"]/, "") }
keys.length == 1 ? [keys.first, nil] : nil
else
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 55bffc6518..00f74318c0 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -407,7 +407,14 @@ module ActiveRecord
def remove_column(table_name, *column_names) #:nodoc:
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
- column_names.flatten.each do |column_name|
+
+ if column_names.flatten!
+ message = 'Passing array to remove_columns is deprecated, please use ' +
+ 'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`'
+ ActiveSupport::Deprecation.warn message, caller
+ end
+
+ column_names.each do |column_name|
alter_table(table_name) do |definition|
definition.columns.delete(definition[column_name])
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index d8340bf1c9..7a5634f225 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -419,11 +419,15 @@ module ActiveRecord
cache_for_connection(connection).update(fixtures_map)
end
- def self.instantiate_fixtures(object, fixture_name, fixtures, load_instances = true)
+ #--
+ # TODO:NOTE: in the next version, the __with_new_arity suffix and
+ # the method with the old arity will be removed.
+ #++
+ def self.instantiate_fixtures__with_new_arity(object, fixture_set, load_instances = true) # :nodoc:
if load_instances
- fixtures.each do |name, fixture|
+ fixture_set.each do |fixture_name, fixture|
begin
- object.instance_variable_set "@#{name}", fixture.find
+ object.instance_variable_set "@#{fixture_name}", fixture.find
rescue FixtureClassNotFound
nil
end
@@ -431,9 +435,24 @@ module ActiveRecord
end
end
+ # The use with parameters <tt>(object, fixture_set_name, fixture_set, load_instances = true)</tt> is deprecated, +fixture_set_name+ parameter is not used.
+ # Use as:
+ #
+ # instantiate_fixtures(object, fixture_set, load_instances = true)
+ def self.instantiate_fixtures(object, fixture_set, load_instances = true, rails_3_2_compatibility_argument = true)
+ unless load_instances == true || load_instances == false
+ ActiveSupport::Deprecation.warn(
+ "ActiveRecord::Fixtures.instantiate_fixtures with parameters (object, fixture_set_name, fixture_set, load_instances = true) is deprecated and shall be removed from future releases. Use it with parameters (object, fixture_set, load_instances = true) instead (skip fixture_set_name).",
+ caller)
+ fixture_set = load_instances
+ load_instances = rails_3_2_compatibility_argument
+ end
+ instantiate_fixtures__with_new_arity(object, fixture_set, load_instances)
+ end
+
def self.instantiate_all_loaded_fixtures(object, load_instances = true)
- all_loaded_fixtures.each do |table_name, fixtures|
- ActiveRecord::Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
+ all_loaded_fixtures.each_value do |fixture_set|
+ ActiveRecord::Fixtures.instantiate_fixtures(object, fixture_set, load_instances)
end
end
@@ -659,9 +678,6 @@ module ActiveRecord
"#{@fixture_path}.yml"
end
- def yaml_fixtures_key(path)
- ::File.basename(@fixture_path).split(".").first
- end
end
class Fixture #:nodoc:
@@ -893,8 +909,8 @@ module ActiveRecord
ActiveRecord::Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
else
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
- @loaded_fixtures.each do |fixture_name, fixtures|
- ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_name, fixtures, load_instances?)
+ @loaded_fixtures.each_value do |fixture_set|
+ ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_set, load_instances?)
end
end
end
diff --git a/activerecord/lib/active_record/identity_map.rb b/activerecord/lib/active_record/identity_map.rb
index 680d9ffea0..b1da547142 100644
--- a/activerecord/lib/active_record/identity_map.rb
+++ b/activerecord/lib/active_record/identity_map.rb
@@ -90,7 +90,7 @@ module ActiveRecord
end
def add(record)
- repository[record.class.symbolized_sti_name][record.id] = record
+ repository[record.class.symbolized_sti_name][record.id] = record if contain_all_columns?(record)
end
def remove(record)
@@ -104,6 +104,12 @@ module ActiveRecord
def clear
repository.clear
end
+
+ private
+
+ def contain_all_columns?(record)
+ (record.class.column_names - record.attribute_names).empty?
+ end
end
# Reinitialize an Identity Map model object from +coder+.
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index de9461982a..3e12a67c2a 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -63,15 +63,7 @@ module ActiveRecord
record_id = sti_class.primary_key && record[sti_class.primary_key]
if ActiveRecord::IdentityMap.enabled? && record_id
- if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
- record_id = record_id.to_i
- end
- if instance = IdentityMap.get(sti_class, record_id)
- instance.reinit_with('attributes' => record)
- else
- instance = sti_class.allocate.init_with('attributes' => record)
- IdentityMap.add(instance)
- end
+ instance = use_identity_map(sti_class, record_id, record)
else
instance = sti_class.allocate.init_with('attributes' => record)
end
@@ -122,6 +114,21 @@ module ActiveRecord
private
+ def use_identity_map(sti_class, record_id, record)
+ if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
+ record_id = record_id.to_i
+ end
+
+ if instance = IdentityMap.get(sti_class, record_id)
+ instance.reinit_with('attributes' => record)
+ else
+ instance = sti_class.allocate.init_with('attributes' => record)
+ IdentityMap.add(instance)
+ end
+
+ instance
+ end
+
def find_sti_class(type_name)
if type_name.blank? || !columns_hash.include?(inheritance_column)
self
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 08cf9fb504..13b7c6e214 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -72,7 +72,13 @@ module ActiveRecord
# and then establishes the connection.
initializer "active_record.initialize_database" do |app|
ActiveSupport.on_load(:active_record) do
- self.configurations = app.config.database_configuration
+ db_connection_type = "DATABASE_URL"
+ unless ENV['DATABASE_URL']
+ db_connection_type = "database.yml"
+ self.configurations = app.config.database_configuration
+ end
+ Rails.logger.info "Connecting to database specified by #{db_connection_type}"
+
establish_connection
end
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 8c56072337..4b3b30d6ed 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -403,6 +403,8 @@ module ActiveRecord
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
# +after_destroy+ callbacks, use the +destroy_all+ method instead.
def delete_all(conditions = nil)
+ raise ActiveRecordError.new("delete_all doesn't support limit scope") if self.limit_value
+
IdentityMap.repository[symbolized_base_class] = {} if IdentityMap.enabled?
if conditions
where(conditions).delete_all
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 9a34927857..4737a194f0 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -185,9 +185,8 @@ module ActiveRecord
# Person.exists?(['name LIKE ?', "%#{query}%"])
# Person.exists?
def exists?(id = false)
- return false if id.nil?
-
id = id.id if ActiveRecord::Base === id
+ return false if id.nil?
join_dependency = construct_join_dependency_for_association_find
relation = construct_relation_for_association_find(join_dependency)
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index 0edc3f1dcc..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
@@ -191,7 +191,7 @@ module ActiveRecord
protected
def valid_scope_name?(name)
- if respond_to?(name, true)
+ if logger && respond_to?(name, true)
logger.warn "Creating scope :#{name}. " \
"Overwriting existing method #{self.name}.#{name}."
end
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 2e2ea8c42b..154aacef9f 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -54,13 +54,13 @@ module ActiveRecord
def build_relation(klass, table, attribute, value) #:nodoc:
column = klass.columns_hash[attribute.to_s]
- value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if column.text?
+ value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if value && column.text?
if !options[:case_sensitive] && value && column.text?
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
relation = klass.connection.case_insensitive_comparison(table, attribute, column, value)
else
- value = klass.connection.case_sensitive_modifier(value)
+ value = klass.connection.case_sensitive_modifier(value) if value
relation = table[attribute].eq(value)
end
@@ -81,7 +81,7 @@ module ActiveRecord
#
# class Person < ActiveRecord::Base
# validates_uniqueness_of :user_name, :scope => :account_id
- # end
+ # end
#
# Or even multiple scope parameters. For example, making sure that a teacher can only be on the schedule once
# per semester for a particular class.
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index cee59b3c0a..cf82eea47a 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -3,7 +3,7 @@ module ActiveRecord
MAJOR = 3
MINOR = 2
TINY = 3
- PRE = "rc2"
+ PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
index 9ca63a209e..b9b5ec7956 100644
--- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
@@ -13,9 +13,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration
<% attributes.each do |attribute| -%>
<%- if migration_action -%>
<%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><%= attribute.inject_options %><% end %>
- <% if attribute.has_index? && migration_action == 'add' %>
+ <%- if attribute.has_index? && migration_action == 'add' -%>
add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
- <% end -%>
+ <%- end -%>
<%- end -%>
<%- end -%>
end
diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
index 146b77a95c..65328522c3 100644
--- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
@@ -52,6 +52,36 @@ module ActiveRecord
assert_equal str, value
end
+ def test_pk_and_sequence_for
+ pk, seq = @conn.pk_and_sequence_for('ex')
+ assert_equal 'id', pk
+ assert_equal @conn.default_sequence_name('ex', 'id'), seq
+ end
+
+ def test_pk_and_sequence_for_with_non_standard_primary_key
+ @conn.exec_query('drop table if exists ex_with_non_standard_pk')
+ @conn.exec_query(<<-eosql)
+ CREATE TABLE `ex_with_non_standard_pk` (
+ `code` INT(11) DEFAULT NULL auto_increment,
+ PRIMARY KEY (`code`))
+ eosql
+ pk, seq = @conn.pk_and_sequence_for('ex_with_non_standard_pk')
+ assert_equal 'code', pk
+ assert_equal @conn.default_sequence_name('ex_with_non_standard_pk', 'code'), seq
+ end
+
+ def test_pk_and_sequence_for_with_custom_index_type_pk
+ @conn.exec_query('drop table if exists ex_with_custom_index_type_pk')
+ @conn.exec_query(<<-eosql)
+ CREATE TABLE `ex_with_custom_index_type_pk` (
+ `id` INT(11) DEFAULT NULL auto_increment,
+ PRIMARY KEY USING BTREE (`id`))
+ eosql
+ pk, seq = @conn.pk_and_sequence_for('ex_with_custom_index_type_pk')
+ assert_equal 'id', pk
+ assert_equal @conn.default_sequence_name('ex_with_custom_index_type_pk', 'id'), seq
+ end
+
private
def insert(ctx, data)
binds = data.map { |name, value|
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 1dc71ac4cc..d7af07f9c7 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1095,4 +1095,28 @@ class EagerAssociationTest < ActiveRecord::TestCase
Post.includes(:comments).order(nil).where(:comments => {:body => "Thank you for the welcome"}).first
end
end
+
+ def test_deep_including_through_habtm
+ posts = Post.find(:all, :include => {:categories => :categorizations}, :order => "posts.id, categories.id")
+ assert_no_queries { assert_equal 2, posts[0].categories[0].categorizations.length }
+ assert_no_queries { assert_equal 1, posts[0].categories[1].categorizations.length }
+ assert_no_queries { assert_equal 2, posts[1].categories[0].categorizations.length }
+ end
+
+ test "scoping with a circular preload" do
+ assert_equal Comment.find(1), Comment.preload(:post => :comments).scoping { Comment.find(1) }
+ end
+
+ test "circular preload does not modify unscoped" do
+ expected = FirstPost.unscoped.find(2)
+ FirstPost.preload(:comments => :first_post).find(1)
+ assert_equal expected, FirstPost.unscoped.find(2)
+ end
+
+ test "preload ignores the scoping" do
+ assert_equal(
+ Comment.find(1).post,
+ Post.where('1 = 0').scoping { Comment.preload(:post).find(1).post }
+ )
+ end
end
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 510411ecb2..bacab8d2f2 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
@@ -379,6 +379,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 0, active_record.developers_by_sql(true).size
end
+ def test_deleting_all_with_sql
+ project = Project.find(1)
+ project.developers_by_sql.delete_all
+ assert_equal 0, project.developers_by_sql.size
+ end
+
def test_deleting_all
david = Developer.find(1)
david.projects.reload
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index f7b2b42959..4f50c97147 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1686,4 +1686,28 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [bulb2], car.bulbs
assert_equal [bulb2], car.reload.bulbs
end
+
+ def test_replace_returns_target
+ car = Car.create(:name => 'honda')
+ bulb1 = car.bulbs.create
+ bulb2 = car.bulbs.create
+ bulb3 = Bulb.create
+
+ assert_equal [bulb1, bulb2], car.bulbs
+ result = car.bulbs.replace([bulb3, bulb1])
+ assert_equal [bulb1, bulb3], car.bulbs
+ assert_equal [bulb1, bulb3], result
+ end
+
+ test "delete_all, when not loaded, doesn't load the records" do
+ post = posts(:welcome)
+
+ assert post.taggings_with_delete_all.count > 0
+ assert !post.taggings_with_delete_all.loaded?
+
+ # 2 queries: one DELETE and another to update the counter cache
+ assert_queries(2) do
+ post.taggings_with_delete_all.delete_all
+ end
+ end
end
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 6f5644ada0..f4592f7d0e 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -403,7 +403,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_polymorphic_has_one
- assert_equal Tagging.find(1,2).sort_by { |t| t.id }, authors(:david).tagging.order(:id)
+ assert_equal Tagging.find(1,2).sort_by { |t| t.id }, authors(:david).tagging.order('taggings.id')
end
def test_has_many_through_polymorphic_has_many
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index acb9a44305..5e5325b570 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -620,6 +620,20 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
+ def test_time_zone_aware_attribute_saved
+ old_default, ActiveRecord::Base.default_timezone = ActiveRecord::Base.default_timezone, :utc
+
+ in_time_zone 1 do
+ record = @target.create(:written_on => '2012-02-20 10:00')
+
+ record.written_on = '2012-02-20 09:00'
+ record.save
+ assert_equal Time.zone.local(2012, 02, 20, 9), record.reload.written_on
+ end
+ ensure
+ ActiveRecord::Base.default_timezone = old_default
+ end
+
def test_setting_time_zone_aware_attribute_to_blank_string_returns_nil
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 4c3f2bda57..315a9b001b 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -626,7 +626,9 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
def test_a_child_marked_for_destruction_should_not_be_destroyed_twice
@pirate.ship.mark_for_destruction
assert @pirate.save
- @pirate.ship.expects(:destroy).never
+ class << @pirate.ship
+ def destroy; raise "Should not be called" end
+ end
assert @pirate.save
end
@@ -671,7 +673,9 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
def test_a_parent_marked_for_destruction_should_not_be_destroyed_twice
@ship.pirate.mark_for_destruction
assert @ship.save
- @ship.pirate.expects(:destroy).never
+ class << @ship.pirate
+ def destroy; raise "Should not be called" end
+ end
assert @ship.save
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index aeb44da2b2..f6497d9bad 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -61,7 +61,11 @@ end
class Weird < ActiveRecord::Base; end
-class Boolean < ActiveRecord::Base; end
+class Boolean < ActiveRecord::Base
+ def has_fun
+ super
+ end
+end
class LintTest < ActiveRecord::TestCase
include ActiveModel::Lint::Tests
@@ -930,6 +934,16 @@ class BasicsTest < ActiveRecord::TestCase
assert b_true.value?
end
+ def test_boolean_without_questionmark
+ b_true = Boolean.create({ "value" => true })
+ true_id = b_true.id
+
+ subclass = Class.new(Boolean).find true_id
+ superclass = Boolean.find true_id
+
+ assert_equal superclass.read_attribute(:has_fun), subclass.read_attribute(:has_fun)
+ end
+
def test_boolean_cast_from_string
b_blank = Boolean.create({ "value" => "" })
blank_id = b_blank.id
@@ -1978,9 +1992,7 @@ class BasicsTest < ActiveRecord::TestCase
original_logger = ActiveRecord::Base.logger
log = StringIO.new
ActiveRecord::Base.logger = Logger.new(log)
- assert_deprecated do
- ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => true) { ActiveRecord::Base.logger.debug "Loud" }
- end
+ ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => true) { ActiveRecord::Base.logger.debug "Loud" }
ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => false) { ActiveRecord::Base.logger.debug "Quiet" }
assert_no_match(/Loud/, log.string)
assert_match(/Quiet/, log.string)
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
index 83c9b6e107..cb7781f8e7 100644
--- a/activerecord/test/cases/explain_test.rb
+++ b/activerecord/test/cases/explain_test.rb
@@ -28,7 +28,9 @@ if ActiveRecord::Base.connection.supports_explain?
original = base.logger
base.logger = nil
- base.logger.expects(:warn).never
+ class << base.logger
+ def warn; raise "Should not be called" end
+ end
with_threshold(0) do
car = Car.where(:name => 'honda').first
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index c72a9dcb17..9bf9fe7d82 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -32,6 +32,7 @@ class FinderTest < ActiveRecord::TestCase
assert Topic.exists?(:author_name => "Mary", :approved => true)
assert Topic.exists?(["parent_id = ?", 1])
assert !Topic.exists?(45)
+ assert !Topic.exists?(Topic.new)
begin
assert !Topic.exists?("foo")
diff --git a/activerecord/test/cases/identity_map_test.rb b/activerecord/test/cases/identity_map_test.rb
index 24132c7617..63d0bcf1fc 100644
--- a/activerecord/test/cases/identity_map_test.rb
+++ b/activerecord/test/cases/identity_map_test.rb
@@ -404,18 +404,13 @@ class IdentityMapTest < ActiveRecord::TestCase
assert comment.save
end
- def test_find_using_select_and_identity_map
- author_id, author = Author.select('id').order(:id).first, Author.order(:id).first
+ def test_do_not_add_to_repository_if_record_does_not_contain_all_columns
+ author = Author.select(:id).first
+ post = author.posts.first
- assert_equal author_id, author
- assert_same author_id, author
- assert_not_nil author.name
-
- post, post_id = Post.order(:id).first, Post.select('id').order(:id).first
-
- assert_equal post_id, post
- assert_same post_id, post
- assert_not_nil post.title
+ assert_nothing_raised do
+ assert_not_nil post.author.name
+ end
end
# Currently AR is not allowing changing primary key (see Persistence#update)
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 5c5c872993..a448e0af9d 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -873,6 +873,20 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_raise(ArgumentError) { Person.connection.remove_column("funny") }
end
+ def test_remove_column_with_array_as_an_argument_is_deprecated
+ ActiveRecord::Base.connection.create_table(:hats) do |table|
+ table.column :hat_name, :string, :limit => 100
+ table.column :hat_size, :integer
+ table.column :hat_style, :string, :limit => 100
+ end
+
+ assert_deprecated /Passing array to remove_columns is deprecated/ do
+ Person.connection.remove_column("hats", ["hat_name", "hat_size"])
+ end
+ ensure
+ ActiveRecord::Base.connection.drop_table(:hats)
+ end
+
def test_change_type_of_not_null_column
assert_nothing_raised do
Topic.connection.change_column "topics", "written_on", :datetime, :null => false
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index bf2807c384..7639585649 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -599,6 +599,7 @@ class RelationTest < ActiveRecord::TestCase
assert ! davids.exists?(authors(:mary).id)
assert ! davids.exists?("42")
assert ! davids.exists?(42)
+ assert ! davids.exists?(davids.new)
fake = Author.where(:name => 'fake author')
assert ! fake.exists?
@@ -643,6 +644,10 @@ class RelationTest < ActiveRecord::TestCase
assert davids.loaded?
end
+ def test_delete_all_limit_error
+ assert_raises(ActiveRecord::ActiveRecordError) { Author.limit(10).delete_all }
+ end
+
def test_select_argument_error
assert_raises(ArgumentError) { Developer.select }
end
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index 0f1b3667cc..8708117129 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -293,4 +293,15 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert w6.errors[:city].any?, "Should have errors for city"
assert_equal ["has already been taken"], w6.errors[:city], "Should have uniqueness message for city"
end
+
+ def test_allow_nil_is_false
+ Topic.validates_uniqueness_of(:title, :allow_nil => false)
+ Topic.destroy_all
+
+ Topic.create!("title" => nil)
+ topic = Topic.new("title" => nil)
+ assert !topic.valid?, "topic should not be valid"
+ assert topic.errors[:title].any?, "Should have errors for title"
+ assert_equal ["has already been taken"], topic.errors[:title], "Should have uniqueness message for title"
+ end
end
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
index 88b139d931..bca937a299 100644
--- a/activerecord/test/models/comment.rb
+++ b/activerecord/test/models/comment.rb
@@ -11,6 +11,8 @@ class Comment < ActiveRecord::Base
belongs_to :post, :counter_cache => true
has_many :ratings
+ belongs_to :first_post, :foreign_key => :post_id
+
has_many :children, :class_name => 'Comment', :foreign_key => :parent_id
belongs_to :parent, :class_name => 'Comment', :counter_cache => :children_count
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index fb4ea4223d..7028b7fd1b 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -90,6 +90,7 @@ ActiveRecord::Schema.define do
create_table :booleans, :force => true do |t|
t.boolean :value
+ t.boolean :has_fun, :null => false, :default => false
end
create_table :bulbs, :force => true do |t|
diff --git a/activeresource/CHANGELOG.md b/activeresource/CHANGELOG.md
index ef957c68ed..e09cd2a772 100644
--- a/activeresource/CHANGELOG.md
+++ b/activeresource/CHANGELOG.md
@@ -1,4 +1,4 @@
-## Rails 3.2.3 (unreleased) ##
+## Rails 3.2.3 (March 30, 2012) ##
* No changes.
diff --git a/activeresource/README.rdoc b/activeresource/README.rdoc
index c86289c5fe..9ab9990bcf 100644
--- a/activeresource/README.rdoc
+++ b/activeresource/README.rdoc
@@ -22,13 +22,13 @@ received and serialized into a usable Ruby object.
== Download and installation
-The latest version of Active Support can be installed with RubyGems:
+The latest version of Active Resource can be installed with RubyGems:
% [sudo] gem install activeresource
Source code can be downloaded as part of the Rails project on GitHub
-* https://github.com/rails/rails/tree/master/activeresource
+* https://github.com/rails/rails/tree/3-2-stable/activeresource
=== Configuration and Usage
@@ -172,7 +172,7 @@ Destruction of a resource can be invoked as a class and instance method of the r
== License
-Active Support is released under the MIT license.
+Active Resource is released under the MIT license.
== Support
diff --git a/activeresource/lib/active_resource.rb b/activeresource/lib/active_resource.rb
index 0794a1a800..5730d7e52a 100644
--- a/activeresource/lib/active_resource.rb
+++ b/activeresource/lib/active_resource.rb
@@ -29,6 +29,7 @@ $:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include
require 'active_support'
require 'active_model'
+require 'active_resource/exceptions'
require 'active_resource/version'
module ActiveResource
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index 10cc727bd9..032a245c3c 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -12,7 +12,6 @@ require 'set'
require 'uri'
require 'active_support/core_ext/uri'
-require 'active_resource/exceptions'
require 'active_resource/connection'
require 'active_resource/formats'
require 'active_resource/schema'
diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb
index 13e26d54c3..d2d7253b78 100644
--- a/activeresource/lib/active_resource/version.rb
+++ b/activeresource/lib/active_resource/version.rb
@@ -3,7 +3,7 @@ module ActiveResource
MAJOR = 3
MINOR = 2
TINY = 3
- PRE = "rc2"
+ PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index f35c147e60..8d8a9d8ad8 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,11 +1,19 @@
-## Rails 3.2.3 (unreleased) ##
+## Rails 3.2.4 (unreleased) ##
+
+* Added #beginning_of_hour and #end_of_hour to Time and DateTime core
+ extensions. *Mark J. Titorenko*
+
+
+## Rails 3.2.3 (March 30, 2012) ##
* No changes.
+
## Rails 3.2.2 (March 1, 2012) ##
* No changes.
+
## Rails 3.2.1 (January 26, 2012) ##
* Documentation fixes and improvements.
@@ -90,6 +98,36 @@
filehandle, or tune your filesystem.
+## Rails 3.1.4 (March 1, 2012) ##
+
+* No changes
+
+
+## Rails 3.1.3 (November 20, 2011) ##
+
+* No changes
+
+
+## Rails 3.1.2 (November 18, 2011) ##
+
+* No changes
+
+
+## Rails 3.1.1 (October 7, 2011) ##
+
+* ruby193: String#prepend is also unsafe *Akira Matsuda*
+
+* Fix obviously breakage of Time.=== for Time subclasses *jeremyevans*
+
+* Added fix so that file store does not raise an exception when cache dir does
+ not exist yet. This can happen if a delete_matched is called before anything
+ is saved in the cache. *Philippe Huibonhoa*
+
+* Fixed performance issue where TimeZone lookups would require tzinfo each time *Tim Lucas*
+
+* ActiveSupport::OrderedHash is now marked as extractable when using Array#extract_options! *Prem Sichanugrist*
+
+
## Rails 3.1.0 (August 30, 2011) ##
* ActiveSupport::Dependencies#load and ActiveSupport::Dependencies#require now
@@ -132,12 +170,38 @@
* JSON decoding now uses the multi_json gem which also vendors a json engine called OkJson. The yaml backend has been removed in favor of OkJson as a default engine for 1.8.x, while the built in 1.9.x json implementation will be used by default. *Josh Kalderimis*
+## Rails 3.0.12 (March 1, 2012) ##
+
+* No changes.
+
+
+## Rails 3.0.11 (November 18, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.10 (August 16, 2011) ##
+
+* Delayed backtrace scrubbing in `load_missing_constant` until we actually
+ raise the exception
+
+
+## Rails 3.0.9 (June 16, 2011) ##
+
+* No changes.
+
+
+## Rails 3.0.8 (June 7, 2011) ##
+
+* No changes.
+
+
## Rails 3.0.7 (April 18, 2011) ##
* Hash.from_xml no longer loses attributes on tags containing only whitespace *André Arko*
-* Rails 3.0.6 (April 5, 2011)
+## Rails 3.0.6 (April 5, 2011)
* No changes.
diff --git a/activesupport/README.rdoc b/activesupport/README.rdoc
index 1ab8e00608..ab1926303c 100644
--- a/activesupport/README.rdoc
+++ b/activesupport/README.rdoc
@@ -14,7 +14,7 @@ The latest version of Active Support can be installed with RubyGems:
Source code can be downloaded as part of the Rails project on GitHub
-* https://github.com/rails/rails/tree/master/activesupport
+* https://github.com/rails/rails/tree/3-2-stable/activesupport
== License
diff --git a/activesupport/lib/active_support/benchmarkable.rb b/activesupport/lib/active_support/benchmarkable.rb
index cc94041a1d..f149a7f0ed 100644
--- a/activesupport/lib/active_support/benchmarkable.rb
+++ b/activesupport/lib/active_support/benchmarkable.rb
@@ -35,7 +35,7 @@ module ActiveSupport
options[:level] ||= :info
result = nil
- ms = Benchmark.ms { result = options[:silence] ? logger.silence { yield } : yield }
+ ms = Benchmark.ms { result = options[:silence] ? silence { yield } : yield }
logger.send(options[:level], '%s (%.1fms)' % [ message, ms ])
result
else
diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
index 268303aaf2..5369348c26 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
@@ -2,30 +2,33 @@ require 'active_support/core_ext/array/extract_options'
# Extends the class object with class and instance accessors for class attributes,
# just like the native attr* accessors for instance attributes.
-#
-# Note that unlike +class_attribute+, if a subclass changes the value then that would
-# also change the value for parent class. Similarly if parent class changes the value
-# then that would change the value of subclasses too.
-#
-# class Person
-# cattr_accessor :hair_colors
-# end
-#
-# Person.hair_colors = [:brown, :black, :blonde, :red]
-# Person.hair_colors # => [:brown, :black, :blonde, :red]
-# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
-#
-# To opt out of the instance writer method, pass :instance_writer => false.
-# To opt out of the instance reader method, pass :instance_reader => false.
-# To opt out of both instance methods, pass :instance_accessor => false.
-#
-# class Person
-# cattr_accessor :hair_colors, :instance_writer => false, :instance_reader => false
-# end
-#
-# Person.new.hair_colors = [:brown] # => NoMethodError
-# Person.new.hair_colors # => NoMethodError
class Class
+ # Defines a class attribute if it's not defined and creates a reader method that
+ # returns the attribute value.
+ #
+ # class Person
+ # cattr_reader :hair_colors
+ # end
+ #
+ # Person.class_variable_set("@@hair_colors", [:brown, :black])
+ # Person.hair_colors # => [:brown, :black]
+ # Person.new.hair_colors # => [:brown, :black]
+ #
+ # The attribute name must be a valid method name in Ruby.
+ #
+ # class Person
+ # cattr_reader :"1_Badname "
+ # end
+ # # => NameError: invalid attribute name
+ #
+ # If you want to opt out the instance reader method, you can pass <tt>:instance_reader => false</tt>
+ # or <tt>:instance_accessor => false</tt>.
+ #
+ # class Person
+ # cattr_reader :hair_colors, :instance_reader => false
+ # end
+ #
+ # Person.new.hair_colors # => NoMethodError
def cattr_reader(*syms)
options = syms.extract_options!
syms.each do |sym|
@@ -49,6 +52,43 @@ class Class
end
end
+ # Defines a class attribute if it's not defined and creates a writer method to allow
+ # assignment to the attribute.
+ #
+ # class Person
+ # cattr_writer :hair_colors
+ # end
+ #
+ # Person.hair_colors = [:brown, :black]
+ # Person.class_variable_get("@@hair_colors") # => [:brown, :black]
+ # Person.new.hair_colors = [:blonde, :red]
+ # Person.class_variable_get("@@hair_colors") # => [:blonde, :red]
+ #
+ # The attribute name must be a valid method name in Ruby.
+ #
+ # class Person
+ # cattr_writer :"1_Badname "
+ # end
+ # # => NameError: invalid attribute name
+ #
+ # If you want to opt out the instance writer method, pass <tt>:instance_writer => false</tt>
+ # or <tt>:instance_accessor => false</tt>.
+ #
+ # class Person
+ # cattr_writer :hair_colors, :instance_writer => false
+ # end
+ #
+ # Person.new.hair_colors = [:blonde, :red] # => NoMethodError
+ #
+ # Also, you can pass a block to set up the attribute with a default value.
+ #
+ # class Person
+ # cattr_writer :hair_colors do
+ # [:brown, :black, :blonde, :red]
+ # end
+ # end
+ #
+ # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
def cattr_writer(*syms)
options = syms.extract_options!
syms.each do |sym|
@@ -73,6 +113,54 @@ class Class
end
end
+ # Defines both class and instance accessors for class attributes.
+ #
+ # class Person
+ # cattr_accessor :hair_colors
+ # end
+ #
+ # Person.hair_colors = [:brown, :black, :blonde, :red]
+ # Person.hair_colors # => [:brown, :black, :blonde, :red]
+ # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
+ #
+ # If a subclass changes the value then that would also change the value for
+ # parent class. Similarly if parent class changes the value then that would
+ # change the value of subclasses too.
+ #
+ # class Male < Person
+ # end
+ #
+ # Male.hair_colors << :blue
+ # Person.hair_colors # => [:brown, :black, :blonde, :red, :blue]
+ #
+ # To opt out of the instance writer method, pass <tt>:instance_writer => false</tt>.
+ # To opt out of the instance reader method, pass <tt>:instance_reader => false</tt>.
+ #
+ # class Person
+ # cattr_accessor :hair_colors, :instance_writer => false, :instance_reader => false
+ # end
+ #
+ # Person.new.hair_colors = [:brown] # => NoMethodError
+ # Person.new.hair_colors # => NoMethodError
+ #
+ # Or pass <tt>:instance_accessor => false</tt>, to opt out both instance methods.
+ #
+ # class Person
+ # cattr_accessor :hair_colors, :instance_accessor => false
+ # end
+ #
+ # Person.new.hair_colors = [:brown] # => NoMethodError
+ # Person.new.hair_colors # => NoMethodError
+ #
+ # Also you can pass a block to set up the attribute with a default value.
+ #
+ # class Person
+ # cattr_accessor :hair_colors do
+ # [:brown, :black, :blonde, :red]
+ # end
+ # end
+ #
+ # Person.class_variable_get("@@hair_colors") #=> [:brown, :black, :blonde, :red]
def cattr_accessor(*syms, &blk)
cattr_reader(*syms)
cattr_writer(*syms, &blk)
diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
index 6f3fd416c1..b759dcc824 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -81,6 +81,17 @@ class DateTime
change(:hour => 23, :min => 59, :sec => 59)
end
+ # Returns a new DateTime representing the start of the hour (hh:00:00)
+ def beginning_of_hour
+ change(:min => 0)
+ end
+ alias :at_beginning_of_hour :beginning_of_hour
+
+ # Returns a new DateTime representing the end of the hour (hh:59:59)
+ def end_of_hour
+ change(:min => 59, :sec => 59)
+ end
+
# 1.9.3 defines + and - on DateTime, < 1.9.3 do not.
if DateTime.public_instance_methods(false).include?(:+)
def plus_with_duration(other) #:nodoc:
diff --git a/activesupport/lib/active_support/core_ext/hash/deep_dup.rb b/activesupport/lib/active_support/core_ext/hash/deep_dup.rb
index 447142605c..c668678ce2 100644
--- a/activesupport/lib/active_support/core_ext/hash/deep_dup.rb
+++ b/activesupport/lib/active_support/core_ext/hash/deep_dup.rb
@@ -1,5 +1,12 @@
class Hash
# Returns a deep copy of hash.
+ #
+ # hash = { :a => { :b => 'b' } }
+ # dup = hash.deep_dup
+ # dup[:a][:c] = 'c'
+ #
+ # hash[:a][:c] #=> nil
+ # dup[:a][:c] #=> "c"
def deep_dup
duplicate = self.dup
duplicate.each_pair do |k,v|
diff --git a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
index af771c86ff..4f61c38eb1 100644
--- a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
+++ b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb
@@ -1,11 +1,16 @@
class Hash
# Returns a new hash with +self+ and +other_hash+ merged recursively.
+ #
+ # h1 = {:x => {:y => [4,5,6]}, :z => [7,8,9]}
+ # h2 = {:x => {:y => [7,8,9]}, :z => "xyz"}
+ #
+ # h1.deep_merge(h2) #=> { :x => {:y => [7, 8, 9]}, :z => "xyz" }
+ # h2.deep_merge(h1) #=> { :x => {:y => [4, 5, 6]}, :z => [7, 8, 9] }
def deep_merge(other_hash)
dup.deep_merge!(other_hash)
end
- # Returns a new hash with +self+ and +other_hash+ merged recursively.
- # Modifies the receiver in place.
+ # Same as +deep_merge+, but modifies +self+.
def deep_merge!(other_hash)
other_hash.each_pair do |k,v|
tv = self[k]
diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb
index d8748b1138..6b7f24eb3e 100644
--- a/activesupport/lib/active_support/core_ext/hash/keys.rb
+++ b/activesupport/lib/active_support/core_ext/hash/keys.rb
@@ -1,10 +1,14 @@
class Hash
# Return a new hash with all keys converted to strings.
+ #
+ # { :name => 'Rob', :years => '28' }.stringify_keys
+ # #=> { "name" => "Rob", "years" => "28" }
def stringify_keys
dup.stringify_keys!
end
- # Destructively convert all keys to strings.
+ # Destructively convert all keys to strings. Same as
+ # +stringify_keys+, but modifies +self+.
def stringify_keys!
keys.each do |key|
self[key.to_s] = delete(key)
@@ -14,12 +18,15 @@ class Hash
# Return a new hash with all keys converted to symbols, as long as
# they respond to +to_sym+.
+ #
+ # { 'name' => 'Rob', 'years' => '28' }.symbolize_keys
+ # #=> { :name => "Rob", :years => "28" }
def symbolize_keys
dup.symbolize_keys!
end
# Destructively convert all keys to symbols, as long as they respond
- # to +to_sym+.
+ # to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
def symbolize_keys!
keys.each do |key|
self[(key.to_sym rescue key) || key] = delete(key)
diff --git a/activesupport/lib/active_support/core_ext/integer/multiple.rb b/activesupport/lib/active_support/core_ext/integer/multiple.rb
index 8dff217ddc..7c6c2f1ca7 100644
--- a/activesupport/lib/active_support/core_ext/integer/multiple.rb
+++ b/activesupport/lib/active_support/core_ext/integer/multiple.rb
@@ -1,5 +1,9 @@
class Integer
# Check whether the integer is evenly divisible by the argument.
+ #
+ # 0.multiple_of?(0) #=> true
+ # 6.multiple_of?(5) #=> false
+ # 10.multiple_of?(2) #=> true
def multiple_of?(number)
number != 0 ? self % number == 0 : zero?
end
diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb
index 9d044eba71..9d1630bb7c 100644
--- a/activesupport/lib/active_support/core_ext/object/duplicable.rb
+++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb
@@ -104,3 +104,16 @@ class Module
false
end
end
+
+require 'bigdecimal'
+class BigDecimal
+ begin
+ BigDecimal.new('4.56').dup
+
+ def duplicable?
+ true
+ end
+ rescue TypeError
+ # can't dup, so use superclass implementation
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index e77a9da0ec..5fb2e31b12 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -7,6 +7,10 @@ class Object
#
# If try is called without a method to call, it will yield any given block with the object.
#
+ # Please also note that +try+ is defined on +Object+, therefore it won't work with
+ # subclasses of +BasicObject+. For example, using try with +SimpleDelegator+ will
+ # delegate +try+ to target instead of calling it on delegator itself.
+ #
# ==== Examples
#
# Without +try+
diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index 0f8933b658..bce0b98366 100644
--- a/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -40,11 +40,23 @@ class String
::Time.send("#{form}_time", *d[0..6]) - d[7]
end
+ # Converts a string to a Date value.
+ #
+ # "1-1-2012".to_date #=> Sun, 01 Jan 2012
+ # "01/01/2012".to_date #=> Sun, 01 Jan 2012
+ # "2012-12-13".to_date #=> Thu, 13 Dec 2012
+ # "12/13/2012".to_date #=> ArgumentError: invalid date
def to_date
return nil if self.blank?
::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday))
end
+ # Converts a string to a DateTime value.
+ #
+ # "1-1-2012".to_datetime #=> Sun, 01 Jan 2012 00:00:00 +0000
+ # "01/01/2012 23:59:59".to_datetime #=> Sun, 01 Jan 2012 23:59:59 +0000
+ # "2012-12-13 12:50".to_datetime #=> Thu, 13 Dec 2012 12:50:00 +0000
+ # "12/13/2012".to_datetime #=> ArgumentError: invalid date
def to_datetime
return nil if self.blank?
d = ::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :sec_fraction).map { |arg| arg || 0 }
diff --git a/activesupport/lib/active_support/core_ext/string/exclude.rb b/activesupport/lib/active_support/core_ext/string/exclude.rb
index 5e184ec1b3..114bcb87f0 100644
--- a/activesupport/lib/active_support/core_ext/string/exclude.rb
+++ b/activesupport/lib/active_support/core_ext/string/exclude.rb
@@ -1,5 +1,10 @@
class String
- # The inverse of <tt>String#include?</tt>. Returns true if the string does not include the other string.
+ # The inverse of <tt>String#include?</tt>. Returns true if the string
+ # does not include the other string.
+ #
+ # "hello".exclude? "lo" #=> false
+ # "hello".exclude? "ol" #=> true
+ # "hello".exclude? ?h #=> false
def exclude?(string)
!include?(string)
end
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 69b98d24d3..5743254462 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -1,5 +1,4 @@
require 'active_support/duration'
-require 'active_support/core_ext/time/zones'
require 'active_support/core_ext/time/conversions'
class Time
@@ -219,6 +218,21 @@ class Time
change(:hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
end
+ # Returns a new Time representing the start of the hour (x:00)
+ def beginning_of_hour
+ change(:min => 0)
+ end
+ alias :at_beginning_of_hour :beginning_of_hour
+
+ # Returns a new Time representing the end of the hour, x:59:59.999999 (.999999999 in ruby1.9)
+ def end_of_hour
+ change(
+ :min => 59,
+ :sec => 59,
+ :usec => 999999.999
+ )
+ end
+
# Returns a new Time representing the start of the month (1st of the month, 0:00)
def beginning_of_month
#self - ((self.mday-1).days + self.seconds_since_midnight)
diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb
index 0c5962858e..b4ed74a7b0 100644
--- a/activesupport/lib/active_support/core_ext/time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/time/zones.rb
@@ -1,3 +1,4 @@
+require 'active_support/core_ext/time/calculations'
require 'active_support/time_with_zone'
class Time
diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb
index cbeb6c0a28..f95eb7a289 100644
--- a/activesupport/lib/active_support/json/decoding.rb
+++ b/activesupport/lib/active_support/json/decoding.rb
@@ -9,7 +9,13 @@ module ActiveSupport
module JSON
class << self
def decode(json, options ={})
- data = MultiJson.decode(json, options)
+ # Can't reliably detect whether MultiJson responds to load, since it's
+ # a reserved word. Use adapter as a proxy for new features.
+ data = if MultiJson.respond_to?(:adapter)
+ MultiJson.load(json, options)
+ else
+ MultiJson.decode(json, options)
+ end
if ActiveSupport.parse_json_times
convert_dates_from(data)
else
@@ -18,12 +24,20 @@ module ActiveSupport
end
def engine
- MultiJson.engine
+ if MultiJson.respond_to?(:adapter)
+ MultiJson.adapter
+ else
+ MultiJson.engine
+ end
end
alias :backend :engine
def engine=(name)
- MultiJson.engine = name
+ if MultiJson.respond_to?(:use)
+ MultiJson.use name
+ else
+ MultiJson.engine = name
+ end
end
alias :backend= :engine=
diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb
index 7d6d047ef6..96718d4bd3 100644
--- a/activesupport/lib/active_support/testing/performance/ruby.rb
+++ b/activesupport/lib/active_support/testing/performance/ruby.rb
@@ -36,7 +36,7 @@ module ActiveSupport
RubyProf.pause
full_profile_options[:runs].to_i.times { run_test(@metric, :profile) }
@data = RubyProf.stop
- @total = @data.threads.values.sum(0) { |method_infos| method_infos.max.total_time }
+ @total = @data.threads.sum(0) { |thread| thread.methods.max.total_time }
end
def record
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 35f400f9df..c7d8fc2e17 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -28,7 +28,7 @@ module ActiveSupport
MAPPING = {
"International Date Line West" => "Pacific/Midway",
"Midway Island" => "Pacific/Midway",
- "Samoa" => "Pacific/Pago_Pago",
+ "American Samoa" => "Pacific/Pago_Pago",
"Hawaii" => "Pacific/Honolulu",
"Alaska" => "America/Juneau",
"Pacific Time (US & Canada)" => "America/Los_Angeles",
@@ -167,7 +167,9 @@ module ActiveSupport
"Marshall Is." => "Pacific/Majuro",
"Auckland" => "Pacific/Auckland",
"Wellington" => "Pacific/Auckland",
- "Nuku'alofa" => "Pacific/Tongatapu"
+ "Nuku'alofa" => "Pacific/Tongatapu",
+ "Tokelau Is." => "Pacific/Fakaofo",
+ "Samoa" => "Pacific/Apia"
}.each { |name, zone| name.freeze; zone.freeze }
MAPPING.freeze
diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb
index 57cd9cc1b3..871af46e3e 100644
--- a/activesupport/lib/active_support/version.rb
+++ b/activesupport/lib/active_support/version.rb
@@ -3,7 +3,7 @@ module ActiveSupport
MAJOR = 3
MINOR = 2
TINY = 3
- PRE = "rc2"
+ PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 0087163faf..4a9b7c8a4a 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -91,6 +91,14 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase
assert_equal DateTime.civil(2005,2,4,23,59,59), DateTime.civil(2005,2,4,10,10,10).end_of_day
end
+ def test_beginning_of_hour
+ assert_equal DateTime.civil(2005,2,4,19,0,0), DateTime.civil(2005,2,4,19,30,10).beginning_of_hour
+ end
+
+ def test_end_of_hour
+ assert_equal DateTime.civil(2005,2,4,19,59,59), DateTime.civil(2005,2,4,19,30,10).end_of_hour
+ end
+
def test_beginning_of_month
assert_equal DateTime.civil(2005,2,1,0,0,0), DateTime.civil(2005,2,22,10,10,10).beginning_of_month
end
diff --git a/activesupport/test/core_ext/duplicable_test.rb b/activesupport/test/core_ext/duplicable_test.rb
index e48e6a7c45..356b849adf 100644
--- a/activesupport/test/core_ext/duplicable_test.rb
+++ b/activesupport/test/core_ext/duplicable_test.rb
@@ -4,17 +4,25 @@ require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/numeric/time'
class DuplicableTest < Test::Unit::TestCase
- RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, BigDecimal.new('4.56'), 5.seconds]
+ RAISE_DUP = [nil, false, true, :symbol, 1, 2.3, 5.seconds]
YES = ['1', Object.new, /foo/, [], {}, Time.now]
NO = [Class.new, Module.new]
+ begin
+ bd = BigDecimal.new('4.56')
+ YES << bd.dup
+ rescue TypeError
+ RAISE_DUP << bd
+ end
+
+
def test_duplicable
(RAISE_DUP + NO).each do |v|
assert !v.duplicable?
end
YES.each do |v|
- assert v.duplicable?
+ assert v.duplicable?, "#{v.class} should be duplicable"
end
(YES + NO).each do |v|
@@ -22,7 +30,7 @@ class DuplicableTest < Test::Unit::TestCase
end
RAISE_DUP.each do |v|
- assert_raises(TypeError) do
+ assert_raises(TypeError, v.class.name) do
v.dup
end
end
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index 5b0ccb850a..c030611de3 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -93,6 +93,10 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
end
+ def test_beginning_of_hour
+ assert_equal Time.local(2005,2,4,19,0,0), Time.local(2005,2,4,19,30,10).beginning_of_hour
+ end
+
def test_beginning_of_month
assert_equal Time.local(2005,2,1,0,0,0), Time.local(2005,2,22,10,10,10).beginning_of_month
end
@@ -127,6 +131,10 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,9,02,0,0,0).end_of_week #sunday
end
+ def test_end_of_hour
+ assert_equal Time.local(2005,2,4,19,59,59,999999.999), Time.local(2005,2,4,19,30,10).end_of_hour
+ end
+
def test_end_of_month
assert_equal Time.local(2005,3,31,23,59,59,999999.999), Time.local(2005,3,20,10,10,10).end_of_month
assert_equal Time.local(2005,2,28,23,59,59,999999.999), Time.local(2005,2,20,10,10,10).end_of_month
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index 9d9e411c28..84afee6c84 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -513,6 +513,20 @@ class TimeWithZoneTest < Test::Unit::TestCase
assert_equal "Fri, 31 Dec 1999 23:59:59 EST -05:00", @twz.end_of_day.inspect
end
+ def test_beginning_of_hour
+ utc = Time.utc(2000, 1, 1, 0, 30)
+ twz = ActiveSupport::TimeWithZone.new(utc, @time_zone)
+ assert_equal "Fri, 31 Dec 1999 19:30:00 EST -05:00", twz.inspect
+ assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", twz.beginning_of_hour.inspect
+ end
+
+ def test_end_of_hour
+ utc = Time.utc(2000, 1, 1, 0, 30)
+ twz = ActiveSupport::TimeWithZone.new(utc, @time_zone)
+ assert_equal "Fri, 31 Dec 1999 19:30:00 EST -05:00", twz.inspect
+ assert_equal "Fri, 31 Dec 1999 19:59:59 EST -05:00", twz.end_of_hour.inspect
+ end
+
def test_since
assert_equal "Fri, 31 Dec 1999 19:00:01 EST -05:00", @twz.since(1).inspect
end
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index b02e952671..d27ea44dd6 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,8 @@
+## Rails 3.2.4 (unreleased) ##
+
+* Add hook for resource route's generator. *Santiago Pastorino*
+
+
## Rails 3.2.3 (unreleased) ##
* No changes.
diff --git a/railties/README.rdoc b/railties/README.rdoc
index ae40600401..eed8745870 100644
--- a/railties/README.rdoc
+++ b/railties/README.rdoc
@@ -17,7 +17,7 @@ The latest version of Railties can be installed with RubyGems:
Source code can be downloaded as part of the Rails project on GitHub
-* https://github.com/rails/rails/tree/master/railties
+* https://github.com/rails/rails/tree/3-2-stable/railties
== License
diff --git a/railties/guides/code/getting_started/Gemfile b/railties/guides/code/getting_started/Gemfile
index 768985070c..582a176a44 100644
--- a/railties/guides/code/getting_started/Gemfile
+++ b/railties/guides/code/getting_started/Gemfile
@@ -35,4 +35,4 @@ gem 'jquery-rails'
# gem 'capistrano'
# To use debugger
-# gem 'ruby-debug19', :require => 'ruby-debug'
+# gem 'debugger'
diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile
index bc85f07ecc..baa83a0f9c 100644
--- a/railties/guides/source/action_controller_overview.textile
+++ b/railties/guides/source/action_controller_overview.textile
@@ -148,18 +148,19 @@ In this case, when a user opens the URL +/clients/active+, +params[:status]+ wil
h4. +default_url_options+
-You can set global default parameters that will be used when generating URLs with +default_url_options+. To do this, define a method with that name in your controller:
+You can set global default parameters for URL generation by defining a method called +default_url_options+ in your controller. Such a method must return a hash with the desired defaults, whose keys must be symbols:
<ruby>
class ApplicationController < ActionController::Base
- # The options parameter is the hash passed in to 'url_for'
- def default_url_options(options)
+ def default_url_options
{:locale => I18n.locale}
end
end
</ruby>
-These options will be used as a starting-point when generating URLs, so it's possible they'll be overridden by +url_for+. Because this method is defined in the controller, you can define it on +ApplicationController+ so it would be used for all URL generation, or you could define it on only one controller for all URLs generated there.
+These options will be used as a starting point when generating URLs, so it's possible they'll be overridden by the options passed in +url_for+ calls.
+
+If you define +default_url_options+ in +ApplicationController+, as in the example above, it would be used for all URL generation. The method can also be defined in one specific controller, in which case it only affects URLs generated there.
h3. Session
diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile
index 6f0d985cac..ceb4862ff5 100644
--- a/railties/guides/source/association_basics.textile
+++ b/railties/guides/source/association_basics.textile
@@ -342,9 +342,9 @@ In designing a data model, you will sometimes find a model that should have a re
<ruby>
class Employee < ActiveRecord::Base
- has_many :subordinates, :class_name => "Employee"
- belongs_to :manager, :class_name => "Employee",
+ has_many :subordinates, :class_name => "Employee",
:foreign_key => "manager_id"
+ belongs_to :manager, :class_name => "Employee"
end
</ruby>
diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile
index 6419d32c13..0e811a2527 100644
--- a/railties/guides/source/caching_with_rails.textile
+++ b/railties/guides/source/caching_with_rails.textile
@@ -257,7 +257,9 @@ However, it's important to note that query caches are created at the start of an
h3. Cache Stores
-Rails provides different stores for the cached data created by action and fragment caches. Page caches are always stored on disk.
+Rails provides different stores for the cached data created by <b>action</b> and <b>fragment</b> caches.
+
+TIP: Page caches are always stored on disk.
h4. Configuration
@@ -267,7 +269,7 @@ You can set up your application's default cache store by calling +config.cache_s
config.cache_store = :memory_store
</ruby>
-Alternatively, you can call +ActionController::Base.cache_store+ outside of a configuration block.
+NOTE: Alternatively, you can call +ActionController::Base.cache_store+ outside of a configuration block.
You can access the cache by calling +Rails.cache+.
@@ -294,7 +296,7 @@ h4. ActiveSupport::Cache::MemoryStore
This cache store keeps entries in memory in the same Ruby process. The cache store has a bounded size specified by the +:size+ options to the initializer (default is 32Mb). When the cache exceeds the allotted size, a cleanup will occur and the least recently used entries will be removed.
<ruby>
-ActionController::Base.cache_store = :memory_store, :size => 64.megabytes
+config.cache_store = :memory_store, :size => 64.megabytes
</ruby>
If you're running multiple Ruby on Rails server processes (which is the case if you're using mongrel_cluster or Phusion Passenger), then your Rails server process instances won't be able to share cache data with each other. This cache store is not appropriate for large application deployments, but can work well for small, low traffic sites with only a couple of server processes or for development and test environments.
@@ -306,7 +308,7 @@ h4. ActiveSupport::Cache::FileStore
This cache store uses the file system to store entries. The path to the directory where the store files will be stored must be specified when initializing the cache.
<ruby>
-ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
+config.cache_store = :file_store, "/path/to/cache/directory"
</ruby>
With this cache store, multiple server processes on the same host can share a cache. Servers processes running on different hosts could share a cache by using a shared file system, but that set up would not be ideal and is not recommended. The cache store is appropriate for low to medium traffic sites that are served off one or two hosts.
@@ -322,7 +324,7 @@ When initializing the cache, you need to specify the addresses for all memcached
The +write+ and +fetch+ methods on this cache accept two additional options that take advantage of features specific to memcached. You can specify +:raw+ to send a value directly to the server with no serialization. The value must be a string or number. You can use memcached direct operation like +increment+ and +decrement+ only on raw values. You can also specify +:unless_exist+ if you don't want memcached to overwrite an existing entry.
<ruby>
-ActionController::Base.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com"
+config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com"
</ruby>
h4. ActiveSupport::Cache::EhcacheStore
@@ -330,7 +332,7 @@ h4. ActiveSupport::Cache::EhcacheStore
If you are using JRuby you can use Terracotta's Ehcache as the cache store for your application. Ehcache is an open source Java cache that also offers an enterprise version with increased scalability, management, and commercial support. You must first install the jruby-ehcache-rails3 gem (version 1.1.0 or later) to use this cache store.
<ruby>
-ActionController::Base.cache_store = :ehcache_store
+config.cache_store = :ehcache_store
</ruby>
When initializing the cache, you may use the +:ehcache_config+ option to specify the Ehcache config file to use (where the default is "ehcache.xml" in your Rails config directory), and the :cache_name option to provide a custom name for your cache (the default is rails_cache).
@@ -359,7 +361,7 @@ h4. ActiveSupport::Cache::NullStore
This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with +Rails.cache+, but caching may interfere with being able to see the results of code changes. With this cache store, all +fetch+ and +read+ operations will result in a miss.
<ruby>
-ActionController::Base.cache_store = :null
+config.cache_store = :null_store
</ruby>
h4. Custom Cache Stores
@@ -369,7 +371,7 @@ You can create your own custom cache store by simply extending +ActiveSupport::C
To use a custom cache store, simple set the cache store to a new instance of the class.
<ruby>
-ActionController::Base.cache_store = MyCacheStore.new
+config.cache_store = MyCacheStore.new
</ruby>
h4. Cache Keys
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index 60034cdda5..3466e1c3f4 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -345,7 +345,7 @@ There are only a few configuration options for Action View, starting with three
Proc.new { |html_tag, instance| %Q(<div class="field_with_errors">#{html_tag}</div>).html_safe }
</ruby>
-* +config.action_view.default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+.
+* +config.action_view.default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+. If you want your form builder class to be loaded after initialization (so it's reloaded on each request in development), you can pass it as a +String+
* +config.action_view.erb_trim_mode+ gives the trim mode to be used by ERB. It defaults to +'-'+. See the "ERB documentation":http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/ for more information.
diff --git a/railties/guides/source/contributing_to_ruby_on_rails.textile b/railties/guides/source/contributing_to_ruby_on_rails.textile
index dadacb9b36..c54c5be63a 100644
--- a/railties/guides/source/contributing_to_ruby_on_rails.textile
+++ b/railties/guides/source/contributing_to_ruby_on_rails.textile
@@ -42,7 +42,7 @@ h4. Install git
Ruby on Rails uses git for source code control. The "git homepage":http://git-scm.com/ has installation instructions. There are a variety of resources on the net that will help you get familiar with git:
-* "Everyday Git":http://www.kernel.org/pub/software/scm/git/docs/everyday.html will teach you just enough about git to get by.
+* "Everyday Git":http://schacon.github.com/git/everyday.html will teach you just enough about git to get by.
* The "PeepCode screencast":https://peepcode.com/products/git on git ($9) is easier to follow.
* "GitHub":http://help.github.com offers links to a variety of git resources.
* "Pro Git":http://progit.org/book/ is an entire book about git with a Creative Commons license.
diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile
index 57c7786636..ef0f496870 100644
--- a/railties/guides/source/debugging_rails_applications.textile
+++ b/railties/guides/source/debugging_rails_applications.textile
@@ -205,7 +205,7 @@ The debugger used by Rails, +ruby-debug+, comes as a gem. To install it, just ru
$ sudo gem install ruby-debug
</shell>
-TIP: If you are using Ruby 1.9, you can install a compatible version of +ruby-debug+ by running +sudo gem install ruby-debug19+
+TIP: If you are using Ruby 1.9, you can install a compatible version of +debugger+ by running +sudo gem install debugger+
In case you want to download a particular version or get the source code, refer to the "project's page on rubyforge":http://rubyforge.org/projects/ruby-debug/.
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index 8f2fa47c8b..0bcd50a1c4 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -10,7 +10,7 @@ you should be familiar with:
endprologue.
-WARNING. This Guide is based on Rails 3.1. Some of the code shown here will not
+WARNING. This Guide is based on Rails 3.2. Some of the code shown here will not
work in earlier versions of Rails.
h3. Guide Assumptions
@@ -23,9 +23,9 @@ prerequisites installed:
* The "Ruby":http://www.ruby-lang.org/en/downloads language version 1.8.7 or higher
TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails
-3.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02
+3.0 and above. 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
-on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 for smooth
+on Rails 3.0 and above, so if you want to use Rails 3.0 or above with 1.9.x jump on 1.9.2 for smooth
sailing.
* The "RubyGems":http://rubyforge.org/frs/?group_id=126 packaging system
@@ -248,7 +248,7 @@ the following:
$ rails --version
</shell>
-If it says something like "Rails 3.1.3" you are ready to continue.
+If it says something like "Rails 3.2.3" you are ready to continue.
h4. Creating the Blog Application
@@ -685,6 +685,7 @@ The model file, +app/models/post.rb+ is about as simple as it can get:
<ruby>
class Post < ActiveRecord::Base
+ attr_accessible :content, :name, :title
end
</ruby>
@@ -692,7 +693,9 @@ There isn't much to this file - but note that the +Post+ class inherits from
+ActiveRecord::Base+. Active Record supplies a great deal of functionality to
your Rails models for free, including basic database CRUD (Create, Read, Update,
Destroy) operations, data validation, as well as sophisticated search support
-and the ability to relate multiple models to one another.
+and the ability to relate multiple models to one another. Another important part
+of this file is +attr_accessible+. It specifies a whitelist of attributes that are
+allowed to be updated in bulk (via +update_attributes+ for instance).
h4. Adding Some Validation
@@ -701,6 +704,8 @@ Open the +app/models/post.rb+ file and edit it:
<ruby>
class Post < ActiveRecord::Base
+ attr_accessible :content, :name, :title
+
validates :name, :presence => true
validates :title, :presence => true,
:length => { :minimum => 5 }
@@ -1218,6 +1223,8 @@ You'll need to edit the +post.rb+ file to add the other side of the association:
<ruby>
class Post < ActiveRecord::Base
+ attr_accessible :content, :name, :title
+
validates :name, :presence => true
validates :title, :presence => true,
:length => { :minimum => 5 }
@@ -1605,6 +1612,8 @@ model, +app/models/post.rb+, as follows:
<ruby>
class Post < ActiveRecord::Base
+ attr_accessible :content, :name, :title
+
validates :name, :presence => true
validates :title, :presence => true,
:length => { :minimum => 5 }
@@ -1686,6 +1695,8 @@ edit tags via posts:
<ruby>
class Post < ActiveRecord::Base
+ attr_accessible :content, :name, :title, :tags_attributes
+
validates :name, :presence => true
validates :title, :presence => true,
:length => { :minimum => 5 }
@@ -1703,6 +1714,10 @@ nested attributes (you'll handle that by displaying a "remove" checkbox on the
view that you'll build shortly). The +:reject_if+ option prevents saving new
tags that do not have any attributes filled in.
+Also note we had to add +:tags_attributes+ to the +attr_accessible+ list. If
+we didn't do this there would be a +MassAssignmentSecurity+ exception when we try to
+update tags through our posts model.
+
We will modify +views/posts/_form.html.erb+ to render a partial to make a tag:
<erb>
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index ada150ceec..8eee82fdf1 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -64,9 +64,13 @@ when 'application', 'runner'
require "rails/commands/#{command}"
when 'new'
- puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
- puts "Type 'rails' for help."
- exit(1)
+ if ARGV.first.in?(['-h', '--help'])
+ require 'rails/commands/application'
+ else
+ puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
+ puts "Type 'rails' for help."
+ exit(1)
+ end
when '--version', '-v'
ARGV.unshift '--version'
diff --git a/railties/lib/rails/engine/commands.rb b/railties/lib/rails/engine/commands.rb
index b71119af77..ffbc0b4bd6 100644
--- a/railties/lib/rails/engine/commands.rb
+++ b/railties/lib/rails/engine/commands.rb
@@ -34,6 +34,10 @@ The common rails commands available for engines are:
destroy Undo code generated with "generate" (short-cut alias: "d")
All commands can be run with -h for more information.
+
+If you want to run any commands that need to be run in context
+of the application, like `rails server` or `rails console`,
+you should do it from application's directory (typically test/dummy).
EOT
exit(1)
end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 27f8d13ce8..c1967093ac 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -57,6 +57,7 @@ module Rails
:orm => false,
:performance_tool => nil,
:resource_controller => :controller,
+ :resource_route => true,
:scaffold_controller => :scaffold_controller,
:stylesheets => true,
:stylesheet_engine => :css,
@@ -182,6 +183,7 @@ module Rails
[
"rails",
+ "resource_route",
"#{orm}:migration",
"#{orm}:model",
"#{orm}:observer",
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index a942c11518..4b7decee5c 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -139,13 +139,13 @@ module Rails
<<-GEMFILE.strip_heredoc
gem 'rails', :path => '#{Rails::Generators::RAILS_DEV_PATH}'
gem 'journey', :git => 'git://github.com/rails/journey.git'
- gem 'arel', :git => 'git://github.com/rails/arel.git'
+ gem 'arel', :git => 'git://github.com/rails/arel.git', :branch => '3-0-stable'
GEMFILE
elsif options.edge?
<<-GEMFILE.strip_heredoc
gem 'rails', :git => 'git://github.com/rails/rails.git', :branch => '3-2-stable'
gem 'journey', :git => 'git://github.com/rails/journey.git'
- gem 'arel', :git => 'git://github.com/rails/arel.git'
+ gem 'arel', :git => 'git://github.com/rails/arel.git', :branch => '3-0-stable'
GEMFILE
else
<<-GEMFILE.strip_heredoc
@@ -188,7 +188,7 @@ module Rails
if RUBY_VERSION < "1.9"
"gem 'ruby-debug'"
else
- "gem 'ruby-debug19', :require => 'ruby-debug'"
+ "gem 'debugger'"
end
end
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index af743a9c51..addc979dff 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -31,10 +31,9 @@ module Rails
# root otherwise uses a default description.
def self.desc(description=nil)
return super if description
- usage = source_root && File.expand_path("../USAGE", source_root)
- @desc ||= if usage && File.exist?(usage)
- ERB.new(File.read(usage)).result(binding)
+ @desc ||= if usage_path
+ ERB.new(File.read(usage_path)).result(binding)
else
"Description:\n Create #{base_name.humanize.downcase} files for #{generator_name} generator."
end
@@ -207,7 +206,8 @@ module Rails
# root, you should use source_root.
def self.default_source_root
return unless base_name && generator_name
- path = File.expand_path(File.join(base_name, generator_name, 'templates'), base_root)
+ return unless default_generator_root
+ path = File.join(default_generator_root, 'templates')
path if File.exists?(path)
end
@@ -371,6 +371,19 @@ module Rails
}
end
+ def self.usage_path
+ paths = [
+ source_root && File.expand_path("../USAGE", source_root),
+ default_generator_root && File.join(default_generator_root, "USAGE")
+ ]
+ paths.compact.detect { |path| File.exists? path }
+ end
+
+ def self.default_generator_root
+ path = File.expand_path(File.join(base_name, generator_name), base_root)
+ path if File.exists?(path)
+ end
+
end
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css
index 3b5cc6648e..3192ec897b 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css
@@ -10,4 +10,4 @@
*
*= require_self
*= require_tree .
-*/
+ */
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index 21e7a4b283..bc5eb12939 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -49,6 +49,9 @@ module <%= app_const_base %>
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
+ # Enable escaping HTML in JSON.
+ config.active_support.escape_html_entities_in_json = true
+
# Use SQL instead of Active Record's schema dumper when creating the database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index 0f571f7c1a..b7a5af178d 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -21,7 +21,7 @@
# Generate digests for assets URLs
config.assets.digest = true
- # Defaults to Rails.root.join("public/assets")
+ # Defaults to nil and saved in location specified by config.assets.prefix
# config.assets.manifest = YOUR_PATH
<%- end -%>
diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb
index c7345f3cfb..3a0586ee43 100644
--- a/railties/lib/rails/generators/rails/resource/resource_generator.rb
+++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb
@@ -14,13 +14,7 @@ module Rails
class_option :actions, :type => :array, :banner => "ACTION ACTION", :default => [],
:desc => "Actions for the resource controller"
- 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
- end
+ hook_for :resource_route, :required => true
end
end
end
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
new file mode 100644
index 0000000000..6a5d62803c
--- /dev/null
+++ b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
@@ -0,0 +1,13 @@
+module Rails
+ module Generators
+ class ResourceRouteGenerator < NamedBase
+ 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
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb
index d81c4c3e1d..2cb34fab7a 100644
--- a/railties/lib/rails/generators/test_case.rb
+++ b/railties/lib/rails/generators/test_case.rb
@@ -135,7 +135,7 @@ module Rails
# Asserts a given migration does not exist. You need to supply an absolute path or a
# path relative to the configured destination:
#
- # assert_no_file "config/random.rb"
+ # assert_no_migration "db/migrate/create_products.rb"
#
def assert_no_migration(relative)
file_name = migration_file_name(relative)
@@ -182,7 +182,7 @@ module Rails
# Asserts the given attribute type gets a proper default value:
#
- # assert_field_type :string, "MyString"
+ # assert_field_default_value :string, "MyString"
#
def assert_field_default_value(attribute_type, value)
assert_equal(value, create_generated_attribute(attribute_type).default)
diff --git a/railties/lib/rails/rack/log_tailer.rb b/railties/lib/rails/rack/log_tailer.rb
index 830d840894..18f22e8089 100644
--- a/railties/lib/rails/rack/log_tailer.rb
+++ b/railties/lib/rails/rack/log_tailer.rb
@@ -4,10 +4,13 @@ module Rails
def initialize(app, log = nil)
@app = app
- path = Pathname.new(log || "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log").cleanpath
- @cursor = ::File.size(path)
+ path = Pathname.new(log || "#{::File.expand_path(Rails.root)}/log/#{Rails.env}.log").cleanpath
- @file = ::File.open(path, 'r')
+ @cursor = @file = nil
+ if ::File.exists?(path)
+ @cursor = ::File.size(path)
+ @file = ::File.open(path, 'r')
+ end
end
def call(env)
@@ -17,6 +20,7 @@ module Rails
end
def tail!
+ return unless @cursor
@file.seek @cursor
unless @file.eof?
diff --git a/railties/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake
index 1c28b2c8e6..1760f526e3 100644
--- a/railties/lib/rails/tasks/documentation.rake
+++ b/railties/lib/rails/tasks/documentation.rake
@@ -8,7 +8,7 @@ end
# Monkey-patch to remove redoc'ing and clobber descriptions to cut down on rake -T noise
class RDocTaskWithoutDescriptions < RDoc::Task
- include ::Rake::DSL
+ include ::Rake::DSL if defined?(::Rake::DSL)
def define
task rdoc_task_name
diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb
index 9cbb7591f5..4f960c58f6 100644
--- a/railties/lib/rails/version.rb
+++ b/railties/lib/rails/version.rb
@@ -3,7 +3,7 @@ module Rails
MAJOR = 3
MINOR = 2
TINY = 3
- PRE = "rc2"
+ PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index a7b9407daf..6101b8647a 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -21,8 +21,12 @@ Gem::Specification.new do |s|
s.rdoc_options << '--exclude' << '.'
s.add_dependency('rake', '>= 0.8.7')
- s.add_dependency('thor', '~> 0.14.6')
s.add_dependency('rack-ssl', '~> 1.3.2')
+
+ # The current API of the Thor gem (0.14) will remain stable at least until Thor 2.0. Because
+ # Thor is so heavily used by other gems, we will accept Thor's semver guarantee to reduce
+ # the possibility of conflicts.
+ s.add_dependency('thor', '>= 0.14.6', '< 2.0')
s.add_dependency('rdoc', '~> 3.4')
s.add_dependency('activesupport', version)
s.add_dependency('actionpack', version)
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index c864d70fa2..673a403fd3 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -110,6 +110,15 @@ module ApplicationTests
assert !File.exists?("#{app_path}/public/assets/something.else.css")
end
+ test "precompile something.js for directory containing index file" do
+ add_to_config "config.assets.precompile = [ 'something.js' ]"
+ app_file "app/assets/javascripts/something/index.js.erb", "alert();"
+
+ precompile!
+
+ assert File.exists?("#{app_path}/public/assets/something.js")
+ end
+
test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do
add_to_config "config.assets.digest = true"
add_to_config "config.action_controller.perform_caching = false"
diff --git a/railties/test/application/initializers/active_record_test.rb b/railties/test/application/initializers/active_record_test.rb
new file mode 100644
index 0000000000..edf78a8a0a
--- /dev/null
+++ b/railties/test/application/initializers/active_record_test.rb
@@ -0,0 +1,62 @@
+require "isolation/abstract_unit"
+require "rack/test"
+
+module ApplicationTests
+ class ActiveRecordTest < Test::Unit::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ @database_url = ENV['DATABASE_URL']
+ ENV.delete('DATABASE_URL')
+ build_app
+ boot_rails
+ end
+
+ def teardown
+ teardown_app
+ ENV['DATABASE_URL'] = @database_url
+ end
+
+ test "blows up when no DATABASE_URL env var or database.yml" do
+ FileUtils.rm_rf("#{app_path}/config/database.yml")
+ boot_rails
+ simple_controller
+
+ get '/foo'
+ assert last_response.body.include?("We're sorry, but something went wrong (500)")
+ end
+
+ test "uses DATABASE_URL env var when config/database.yml doesn't exist" do
+ database_path = "/db/foo.sqlite3"
+ FileUtils.rm_rf("#{app_path}/config/database.yml")
+ ENV['DATABASE_URL'] = "sqlite3://#{database_path}"
+ simple_controller
+
+ get '/foo'
+ assert_equal 'foo', last_response.body
+
+ # clean up
+ FileUtils.rm("#{app_path}/#{database_path}")
+ end
+
+ test "DATABASE_URL env var takes precedence over config/database.yml" do
+ database_path = "/db/foo.sqlite3"
+ ENV['DATABASE_URL'] = "sqlite3://#{database_path}"
+ simple_controller
+
+ get '/foo'
+ assert File.read("#{app_path}/log/production.log").include?("DATABASE_URL")
+
+ # clean up
+ FileUtils.rm("#{app_path}/#{database_path}")
+ end
+
+ test "logs the use of config/database.yml" do
+ simple_controller
+
+ get '/foo'
+ assert File.read("#{app_path}/log/production.log").include?("database.yml")
+ end
+ end
+end
diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb
index 9f39e18a74..aee2417abc 100644
--- a/railties/test/application/middleware/session_test.rb
+++ b/railties/test/application/middleware/session_test.rb
@@ -26,5 +26,25 @@ module ApplicationTests
require "#{app_path}/config/environment"
assert app.config.session_options[:secure], "Expected session to be marked as secure"
end
+
+ test "session is not loaded if it's not used" do
+ make_basic_app
+
+ class ::OmgController < ActionController::Base
+ def index
+ if params[:flash]
+ flash[:notice] = "notice"
+ end
+
+ render :nothing => true
+ end
+ end
+
+ get "/?flash=true"
+ get "/"
+
+ assert last_request.env["HTTP_COOKIE"]
+ assert !last_response.headers["Set-Cookie"]
+ end
end
end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 37703d9376..5bf1dbda8d 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -84,6 +84,16 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_equal false, $?.success?
end
+ def test_application_new_show_help_message_inside_existing_rails_directory
+ app_root = File.join(destination_root, 'myfirstapp')
+ run_generator [app_root]
+ output = Dir.chdir(app_root) do
+ `rails new --help`
+ end
+ assert_match /rails new APP_PATH \[options\]/, output
+ assert_equal true, $?.success?
+ end
+
def test_application_name_is_detected_if_it_exists_and_app_folder_renamed
app_root = File.join(destination_root, "myapp")
app_moved_root = File.join(destination_root, "myapp_moved")
@@ -298,10 +308,10 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
- def test_inclusion_of_ruby_debug19_if_ruby19
+ def test_inclusion_of_debugger_if_ruby19
run_generator
assert_file "Gemfile" do |contents|
- assert_match(/gem 'ruby-debug19', :require => 'ruby-debug'/, contents) unless RUBY_VERSION < '1.9'
+ assert_match(/gem 'debugger'/, contents) unless RUBY_VERSION < '1.9'
end
end
@@ -316,7 +326,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_default_usage
- File.expects(:exist?).returns(false)
+ Rails::Generators::AppGenerator.expects(:usage_path).returns(nil)
assert_match(/Create rails files for app generator/, Rails::Generators::AppGenerator.desc)
end
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index 16e4ac0881..a89631d022 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -157,4 +157,8 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
end
end
end
+
+ def test_properly_identifies_usage_file
+ assert generator_class.send(:usage_path)
+ end
end
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index b262951b88..c072aaab26 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -127,7 +127,7 @@ module SharedGeneratorTests
quietly { generator.invoke_all }
assert_file 'Gemfile', %r{^gem\s+["']rails["'],\s+:git\s+=>\s+["']#{Regexp.escape("git://github.com/rails/rails.git")}["'],\s+:branch\s+=>\s+["']3-2-stable["']$}
assert_file 'Gemfile', %r{^gem\s+["']journey["'],\s+:git\s+=>\s+["']#{Regexp.escape("git://github.com/rails/journey.git")}["']$}
- assert_file 'Gemfile', %r{^gem\s+["']arel["'],\s+:git\s+=>\s+["']#{Regexp.escape("git://github.com/rails/arel.git")}["']$}
+ assert_file 'Gemfile', %r{^gem\s+["']arel["'],\s+:git\s+=>\s+["']#{Regexp.escape("git://github.com/rails/arel.git")}["'].*$}
end
def test_skip_gemfile
diff --git a/version.rb b/version.rb
index 9cbb7591f5..4f960c58f6 100644
--- a/version.rb
+++ b/version.rb
@@ -3,7 +3,7 @@ module Rails
MAJOR = 3
MINOR = 2
TINY = 3
- PRE = "rc2"
+ PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end