aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.travis.yml5
-rw-r--r--Gemfile10
-rw-r--r--RAILS_VERSION2
-rw-r--r--actionmailer/CHANGELOG.md50
-rw-r--r--actionmailer/actionmailer.gemspec2
-rw-r--r--actionmailer/lib/action_mailer/base.rb22
-rw-r--r--actionmailer/lib/action_mailer/version.rb2
-rw-r--r--actionmailer/test/base_test.rb6
-rw-r--r--actionmailer/test/mailers/base_mailer.rb5
-rw-r--r--actionpack/CHANGELOG.md222
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb3
-rw-r--r--actionpack/lib/action_controller/metal/data_streaming.rb2
-rw-r--r--actionpack/lib/action_controller/metal/hide_actions.rb10
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb2
-rw-r--r--actionpack/lib/action_controller/test_case.rb29
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb10
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb12
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/best_standards_support.rb10
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb11
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb30
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb6
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb29
-rw-r--r--actionpack/lib/action_pack/version.rb2
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb15
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb29
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/record_tag_helper.rb4
-rw-r--r--actionpack/lib/action_view/lookup_context.rb15
-rw-r--r--actionpack/lib/action_view/renderer/abstract_renderer.rb6
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb4
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb5
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb16
-rw-r--r--actionpack/lib/sprockets/helpers/rails_helper.rb29
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb37
-rw-r--r--actionpack/test/controller/base_test.rb12
-rw-r--r--actionpack/test/controller/layout_test.rb16
-rw-r--r--actionpack/test/controller/localized_templates_test.rb9
-rw-r--r--actionpack/test/controller/new_base/render_template_test.rb10
-rw-r--r--actionpack/test/controller/render_test.rb3
-rw-r--r--actionpack/test/controller/routing_test.rb20
-rw-r--r--actionpack/test/controller/send_file_test.rb14
-rw-r--r--actionpack/test/dispatch/best_standards_support_test.rb35
-rw-r--r--actionpack/test/dispatch/prefix_generation_test.rb5
-rw-r--r--actionpack/test/dispatch/request/json_params_parsing_test.rb7
-rw-r--r--actionpack/test/dispatch/request_test.rb37
-rw-r--r--actionpack/test/dispatch/routing/route_set_test.rb86
-rw-r--r--actionpack/test/dispatch/routing_test.rb43
-rw-r--r--actionpack/test/fixtures/sprockets/app/javascripts/extra.js1
-rw-r--r--actionpack/test/fixtures/sprockets/app/stylesheets/extra.css1
-rw-r--r--actionpack/test/fixtures/test/_changing_priority.html.erb1
-rw-r--r--actionpack/test/fixtures/test/_changing_priority.json.erb1
-rw-r--r--actionpack/test/fixtures/test/_first_json_partial.json.erb1
-rw-r--r--actionpack/test/fixtures/test/_json_change_priority.json.erb0
-rw-r--r--actionpack/test/fixtures/test/_second_json_partial.json.erb1
-rw-r--r--actionpack/test/fixtures/test/change_priority.html.erb2
-rw-r--r--actionpack/test/fixtures/test/hello_world_with_partial.html.erb2
-rw-r--r--actionpack/test/fixtures/test/html_template.html.erb1
-rw-r--r--actionpack/test/template/date_helper_i18n_test.rb15
-rw-r--r--actionpack/test/template/date_helper_test.rb31
-rw-r--r--actionpack/test/template/form_helper_test.rb37
-rw-r--r--actionpack/test/template/form_options_helper_test.rb8
-rw-r--r--actionpack/test/template/html-scanner/sanitizer_test.rb17
-rw-r--r--actionpack/test/template/number_helper_test.rb5
-rw-r--r--actionpack/test/template/record_tag_helper_test.rb8
-rw-r--r--actionpack/test/template/render_test.rb17
-rw-r--r--actionpack/test/template/sprockets_helper_test.rb23
-rw-r--r--actionpack/test/template/sprockets_helper_with_routes_test.rb4
-rw-r--r--actionpack/test/template/template_test.rb17
-rw-r--r--activemodel/CHANGELOG.md33
-rw-r--r--activemodel/lib/active_model/errors.rb2
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb7
-rw-r--r--activemodel/lib/active_model/validations.rb2
-rw-r--r--activemodel/lib/active_model/version.rb2
-rw-r--r--activemodel/test/cases/serializers/xml_serialization_test.rb12
-rw-r--r--activerecord/CHANGELOG.md350
-rw-r--r--activerecord/lib/active_record/associations/association.rb15
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb3
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb19
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb10
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb6
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb18
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb20
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb9
-rw-r--r--activerecord/lib/active_record/base.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb26
-rw-r--r--activerecord/lib/active_record/connection_adapters/schema_cache.rb21
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb10
-rw-r--r--activerecord/lib/active_record/explain.rb13
-rw-r--r--activerecord/lib/active_record/integration.rb13
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb2
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb6
-rw-r--r--activerecord/lib/active_record/migration.rb4
-rw-r--r--activerecord/lib/active_record/model_schema.rb4
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb7
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/lib/active_record/query_cache.rb12
-rw-r--r--activerecord/lib/active_record/railties/databases.rake33
-rw-r--r--activerecord/lib/active_record/relation.rb2
-rw-r--r--activerecord/lib/active_record/relation/batches.rb4
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb14
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb8
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb6
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb9
-rw-r--r--activerecord/lib/active_record/result.rb12
-rw-r--r--activerecord/lib/active_record/scoping/named.rb4
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb2
-rw-r--r--activerecord/lib/active_record/version.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/sql_types_test.rb18
-rw-r--r--activerecord/test/cases/adapters/sqlite3/copy_table_test.rb8
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb6
-rw-r--r--activerecord/test/cases/ar_schema_test.rb37
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb22
-rw-r--r--activerecord/test/cases/associations/eager_test.rb20
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb8
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb16
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb6
-rw-r--r--activerecord/test/cases/base_test.rb76
-rw-r--r--activerecord/test/cases/batches_test.rb23
-rw-r--r--activerecord/test/cases/bind_parameter_test.rb19
-rw-r--r--activerecord/test/cases/calculations_test.rb31
-rw-r--r--activerecord/test/cases/column_test.rb26
-rw-r--r--activerecord/test/cases/dirty_test.rb29
-rw-r--r--activerecord/test/cases/dup_test.rb9
-rw-r--r--activerecord/test/cases/explain_test.rb10
-rw-r--r--activerecord/test/cases/finder_test.rb5
-rw-r--r--activerecord/test/cases/helper.rb2
-rw-r--r--activerecord/test/cases/locking_test.rb12
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb34
-rw-r--r--activerecord/test/cases/method_scoping_test.rb10
-rw-r--r--activerecord/test/cases/migration_test.rb44
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb11
-rw-r--r--activerecord/test/cases/persistence_test.rb11
-rw-r--r--activerecord/test/cases/query_cache_test.rb11
-rw-r--r--activerecord/test/cases/quoting_test.rb14
-rw-r--r--activerecord/test/cases/relation/where_test.rb25
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb6
-rw-r--r--activerecord/test/cases/relation_test.rb6
-rw-r--r--activerecord/test/cases/relations_test.rb21
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb16
-rw-r--r--activerecord/test/fixtures/peoples_treasures.yml3
-rw-r--r--activerecord/test/migrations/10_urban/9_add_expressions.rb11
-rw-r--r--activerecord/test/models/bulb.rb2
-rw-r--r--activerecord/test/models/developer.rb14
-rw-r--r--activerecord/test/models/person.rb27
-rw-r--r--activerecord/test/models/possession.rb3
-rw-r--r--activerecord/test/models/post.rb3
-rw-r--r--activerecord/test/models/topic.rb6
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb5
-rw-r--r--activerecord/test/schema/schema.rb14
-rw-r--r--activeresource/CHANGELOG.md28
-rw-r--r--activeresource/lib/active_resource/version.rb2
-rw-r--r--activeresource/test/abstract_unit.rb14
-rw-r--r--activeresource/test/cases/base_test.rb15
-rw-r--r--activesupport/CHANGELOG.md52
-rw-r--r--activesupport/activesupport.gemspec2
-rw-r--r--activesupport/lib/active_support/concern.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/file/atomic.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/hash/indifferent_access.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/hash/slice.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/reporting.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/time/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/time/marshal.rb4
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb2
-rw-r--r--activesupport/lib/active_support/json/encoding.rb10
-rw-r--r--activesupport/lib/active_support/test_case.rb2
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb12
-rw-r--r--activesupport/lib/active_support/testing/mochaing.rb4
-rw-r--r--activesupport/lib/active_support/testing/performance/ruby.rb2
-rw-r--r--activesupport/lib/active_support/testing/setup_and_teardown.rb11
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb16
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb22
-rw-r--r--activesupport/lib/active_support/version.rb2
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb6
-rw-r--r--activesupport/test/abstract_unit.rb2
-rw-r--r--activesupport/test/caching_test.rb1
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb4
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb6
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb86
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb10
-rw-r--r--activesupport/test/fixtures/xml/jdom_doctype.dtd1
-rw-r--r--activesupport/test/fixtures/xml/jdom_entities.txt1
-rw-r--r--activesupport/test/fixtures/xml/jdom_include.txt1
-rw-r--r--activesupport/test/json/encoding_test.rb38
-rw-r--r--activesupport/test/ordered_hash_test.rb1
-rw-r--r--activesupport/test/test_case_test.rb3
-rw-r--r--activesupport/test/time_zone_test.rb56
-rw-r--r--activesupport/test/xml_mini/jdom_engine_test.rb39
-rw-r--r--railties/CHANGELOG.md43
-rw-r--r--railties/guides/source/action_mailer_basics.textile2
-rw-r--r--railties/guides/source/active_record_validations_callbacks.textile8
-rw-r--r--railties/guides/source/active_support_core_extensions.textile4
-rw-r--r--railties/guides/source/ajax_on_rails.textile267
-rw-r--r--railties/guides/source/caching_with_rails.textile4
-rw-r--r--railties/guides/source/configuring.textile2
-rw-r--r--railties/guides/source/getting_started.textile2
-rw-r--r--railties/guides/source/performance_testing.textile2
-rw-r--r--railties/lib/rails/application.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/README16
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb32
-rwxr-xr-xrailties/lib/rails/generators/rails/plugin_new/templates/Rakefile2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/model/model_generator.rb12
-rw-r--r--railties/lib/rails/generators/test_unit/model/templates/fixtures.yml4
-rw-r--r--railties/lib/rails/version.rb2
-rw-r--r--railties/test/application/assets_test.rb1
-rw-r--r--railties/test/application/initializers/active_record_test.rb15
-rw-r--r--railties/test/application/middleware/static_test.rb31
-rw-r--r--railties/test/application/rack/logger_test.rb1
-rw-r--r--railties/test/application/rake_test.rb12
-rw-r--r--railties/test/application/route_inspect_test.rb6
-rw-r--r--railties/test/application/routing_test.rb71
-rw-r--r--railties/test/generators/model_generator_test.rb21
-rw-r--r--railties/test/generators/plugin_new_generator_test.rb6
-rw-r--r--railties/test/generators_test.rb4
-rw-r--r--version.rb2
234 files changed, 2833 insertions, 850 deletions
diff --git a/.gitignore b/.gitignore
index 52a431867c..24f9e9308d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,7 @@
debug.log
.Gemfile
/.bundle
-/.rbenv-version
-/.rvmrc
+/.ruby-version
/Gemfile.lock
/pkg
/dist
diff --git a/.travis.yml b/.travis.yml
index 88999fe831..e130252522 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,11 @@
script: 'ci/travis.rb'
+before_install:
+ - gem install bundler
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
+ - 2.0.0
env:
- "GEM=railties"
- "GEM=ap,am,amo,ares,as"
@@ -21,5 +24,5 @@ notifications:
on_success: change
on_failure: always
rooms:
- - secure: "CGWvthGkBKNnTnk9YSmf9AXKoiRI33fCl5D3jU4nx3cOPu6kv2R9nMjt9EAo\nOuS4Q85qNSf4VNQ2cUPNiNYSWQ+XiTfivKvDUw/QW9r1FejYyeWarMsSBWA+\n0fADjF1M2dkDIVLgYPfwoXEv7l+j654F1KLKB69F0F/netwP9CQ="
+ - secure: "YA1alef1ESHWGFNVwvmVGCkMe4cUy4j+UcNvMUESraceiAfVyRMAovlQBGs6\n9kBRm7DHYBUXYC2ABQoJbQRLDr/1B5JPf/M8+Qd7BKu8tcDC03U01SMHFLpO\naOs/HLXcDxtnnpL07tGVsm0zhMc5N8tq4/L3SHxK7Vi+TacwQzI="
bundler_args: --path vendor/bundle
diff --git a/Gemfile b/Gemfile
index 8c381e9c9e..6098cf78a1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -22,7 +22,7 @@ end
gem 'uglifier', '>= 1.0.3', :require => false
gem 'rake', '>= 0.8.7'
-gem 'mocha', '>= 0.12.1'
+gem 'mocha', '>= 0.13.0', :require => false
group :doc do
# The current sdoc cannot generate GitHub links due
@@ -47,7 +47,7 @@ instance_eval File.read '.Gemfile' if File.exists? '.Gemfile'
platforms :mri do
group :test do
- gem 'ruby-prof', '~> 0.11.2'
+ gem 'ruby-prof', '~> 0.11.2' if RUBY_VERSION < '2.0'
end
end
@@ -67,7 +67,7 @@ end
platforms :jruby do
gem 'json'
- gem 'activerecord-jdbcsqlite3-adapter', '>= 1.2.0'
+ gem 'activerecord-jdbcsqlite3-adapter', '>= 1.2.7'
# This is needed by now to let tests work on JRuby
# TODO: When the JRuby guys merge jruby-openssl in
@@ -75,8 +75,8 @@ platforms :jruby do
gem 'jruby-openssl'
group :db do
- gem 'activerecord-jdbcmysql-adapter', '>= 1.2.0'
- gem 'activerecord-jdbcpostgresql-adapter', '>= 1.2.0'
+ gem 'activerecord-jdbcmysql-adapter', '>= 1.2.7'
+ gem 'activerecord-jdbcpostgresql-adapter', '>= 1.2.7'
end
end
diff --git a/RAILS_VERSION b/RAILS_VERSION
index 17ce91803c..d883a10059 100644
--- a/RAILS_VERSION
+++ b/RAILS_VERSION
@@ -1 +1 @@
-3.2.11
+3.2.13
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index fffadd18a0..ce37730c44 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,11 +1,53 @@
-## Rails 3.2.10 ##
+## unreleased ##
+
+* No changes.
+
+
+## Rails 3.2.13 (Mar 18, 2013) ##
+
+* No changes.
+
+
+## Rails 3.2.12 (Feb 11, 2013) ##
+
+* No changes.
+
+
+## Rails 3.2.11 (Jan 8, 2013) ##
+
+* No changes.
+
+
+## Rails 3.2.10 (Jan 2, 2013) ##
+
+* No changes.
+
+
+## Rails 3.2.9 (Nov 12, 2012) ##
+
+* The return value from mailer methods is no longer relevant. This fixes a bug,
+ which was introduced with 3.2.9.
+ Backport #8450
+ Fix #8448
+
+ class ExampleMailer < ActionMailer::Base
+ # in 3.2.9, returning a falsy value from a mailer action, prevented the email from beeing sent.
+ # With 3.2.10 the return value is no longer relevant. If you call mail() the email will be sent.
+ def nil_returning_mailer_action
+ mail()
+ nil
+ end
+ end
+
+ *Yves Senn*
+
## Rails 3.2.9 (Nov 12, 2012) ##
-* Do not render views when mail() isn't called.
- Fix #7761
+* Do not render views when mail() isn't called.
+ Fix #7761
- *Yves Senn*
+ *Yves Senn*
## Rails 3.2.8 (Aug 9, 2012) ##
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index 3603017814..c2c8e9b8e2 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -17,5 +17,5 @@ Gem::Specification.new do |s|
s.requirements << 'none'
s.add_dependency('actionpack', version)
- s.add_dependency('mail', '~> 2.4.4')
+ s.add_dependency('mail', '~> 2.5.3')
end
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index a9fb49a303..9e2f640915 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -123,8 +123,8 @@ module ActionMailer #:nodoc:
#
# <%= users_url(:host => "example.com") %>
#
- # You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
- # <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
+ # You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
+ # <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
# have no concept of a current URL from which to determine a relative path.
#
# It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt>
@@ -133,7 +133,7 @@ module ActionMailer #:nodoc:
# config.action_mailer.default_url_options = { :host => "example.com" }
#
# When you decide to set a default <tt>:host</tt> for your mailers, then you need to make sure to use the
- # <tt>:only_path => false</tt> option when using <tt>url_for</tt>. Since the <tt>url_for</tt> view helper
+ # <tt>:only_path => false</tt> option when using <tt>url_for</tt>. Since the <tt>url_for</tt> view helper
# will generate relative URLs by default when a <tt>:host</tt> option isn't explicitly provided, passing
# <tt>:only_path => false</tt> will ensure that absolute URLs are generated.
#
@@ -150,8 +150,8 @@ module ActionMailer #:nodoc:
#
# = Multipart Emails
#
- # Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
- # multipart templates, where each template is named after the name of the action, followed by the content
+ # Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
+ # multipart templates, where each template is named after the name of the action, followed by the content
# type. Each such detected template will be added as a separate part to the message.
#
# For example, if the following templates exist:
@@ -448,6 +448,7 @@ module ActionMailer #:nodoc:
# method, for instance).
def initialize(method_name=nil, *args)
super()
+ @mail_was_called = false
@_message = Mail.new
process(method_name, *args) if method_name
end
@@ -455,10 +456,8 @@ module ActionMailer #:nodoc:
def process(*args) #:nodoc:
lookup_context.skip_default_locale!
- generated_mail = super
- unless generated_mail
- @_message = NullMail.new
- end
+ super
+ @_message = NullMail.new unless @mail_was_called
end
class NullMail #:nodoc:
@@ -616,8 +615,9 @@ module ActionMailer #:nodoc:
# end
#
def mail(headers={}, &block)
- # Guard flag to prevent both the old and the new API from firing
- # Should be removed when old API is removed
+ # Guard flag to prevent both the old and the new API from firing.
+ # On master this flag was renamed to `@_mail_was_called`.
+ # On master there is only one API and this flag is no longer used as a guard.
@mail_was_called = true
m = @_message
diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb
index 695ea004f7..00f0516066 100644
--- a/actionmailer/lib/action_mailer/version.rb
+++ b/actionmailer/lib/action_mailer/version.rb
@@ -2,7 +2,7 @@ module ActionMailer
module VERSION #:nodoc:
MAJOR = 3
MINOR = 2
- TINY = 11
+ TINY = 13
PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index b69b26faf0..f648cb1546 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -477,6 +477,12 @@ class BaseTest < ActiveSupport::TestCase
mail.deliver
end
+ test 'the return value of mailer methods is not relevant' do
+ mail = BaseMailer.with_nil_as_return_value
+ assert_equal('Welcome', mail.body.to_s.strip)
+ mail.deliver
+ end
+
# Before and After hooks
class MyObserver
diff --git a/actionmailer/test/mailers/base_mailer.rb b/actionmailer/test/mailers/base_mailer.rb
index 8c4430b046..50438ead2a 100644
--- a/actionmailer/test/mailers/base_mailer.rb
+++ b/actionmailer/test/mailers/base_mailer.rb
@@ -118,4 +118,9 @@ class BaseMailer < ActionMailer::Base
def without_mail_call
end
+
+ def with_nil_as_return_value(hash = {})
+ mail(:template_name => "welcome")
+ nil
+ end
end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index d07ef733aa..9d50342867 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,8 +1,224 @@
-## Rails 3.2.11 ##
+## unreleased ##
-* Strip nils from collections on JSON and XML posts. [CVE-2013-0155]
+* Fix explicit names on multiple file fields. If a file field tag has
+ the multiple option, it is turned into an array field (appending `[]`),
+ but if an explicit name is passed to `file_field` the `[]` is not
+ appended.
+ Fixes #9830.
+
+ *Ryan McGeary*
+
+* Fix assets loading performance in 3.2.13.
+
+ Issue #8756 uses Sprockets for resolving files that already exist on disk,
+ for those files their extensions don't need to be rewritten.
+
+ Fixes #9803.
+
+ *Fred Wu*
+
+* Fix `ActionController#action_missing` not being called.
+ Fixes #9799.
+
+ *Janko Luin*
+
+* `ActionView::Helpers::NumberHelper#number_to_human` returns the number unaltered when
+ the units hash does not contain the needed key, e.g. when the number provided is less
+ than the largest key provided.
+
+ Examples:
+
+ number_to_human(123, units: {}) # => 123
+ number_to_human(123, units: { thousand: 'k' }) # => 123
+
+ Fixes #9269.
+ Backport #9347.
+
+ *Michael Hoffman*
+
+* Include I18n locale fallbacks in view lookup.
+ Fixes GH#3512.
+
+ *Juan Barreneche*
+
+* Fix `ActionDispatch::Request#formats` when the Accept request-header is an
+ empty string. Fix #7774 [Backport #8977, #9541]
+
+ *Soylent + Maxime Réty*
+
+
+## Rails 3.2.13 (Mar 18, 2013) ##
+
+* Fix incorrectly appended square brackets to a multiple select box
+ if an explicit name has been given and it already ends with "[]".
+
+ Before:
+
+ select(:category, [], {}, multiple: true, name: "post[category][]")
+ # => <select name="post[category][][]" ...>
+
+ After:
+
+ select(:category, [], {}, multiple: true, name: "post[category][]")
+ # => <select name="post[category][]" ...>
+
+ Backport #9616.
+
+ *Olek Janiszewski*
+
+* Determine the controller#action from only the matched path when using the
+ shorthand syntax. Previously the complete path was used, which led
+ to problems with nesting (scopes and namespaces).
+ Fixes #7554.
+ Backport #9361.
+
+ Example:
+
+ # this will route to questions#new
+ scope ':locale' do
+ get 'questions/new'
+ end
+
+ *Yves Senn*
+
+* Fix `assert_template` with `render :stream => true`.
+ Fix #1743.
+ Backport #5288.
+
+ *Sergey Nartimov*
+
+* Eagerly populate the http method lookup cache so local project inflections do
+ not interfere with use of underscore method ( and we don't need locks )
+
+ *Aditya Sanghi*
+
+* `BestStandardsSupport` no longer duplicates `X-UA-Compatible` values on
+ each request to prevent header size from blowing up.
+
+ *Edward Anderson*
+
+* Fixed JSON params parsing regression for non-object JSON content.
+
+ *Dylan Smith*
+
+* Prevent unnecessary asset compilation when using `javascript_include_tag` on
+ files with non-standard extensions.
+
+ *Noah Silas*
+
+* Fixes issue where duplicate assets can be required with sprockets.
+
+ *Jeremy Jackson*
+
+* Bump `rack` dependency to 1.4.3, eliminate `Rack::File` headers deprecation warning.
+
+ *Sam Ruby + Carlos Antonio da Silva*
+
+* Do not append second slash to `root_url` when using `trailing_slash: true`
+
+ Fix #8700.
+ Backport #8701.
+
+ Example:
+ # before
+ root_url # => http://test.host//
+
+ # after
+ root_url # => http://test.host/
+
+ *Yves Senn*
+
+* Fix a bug in `content_tag_for` that prevents it for work without a block.
+
+ *Jasl*
+
+* Clear url helper methods when routes are reloaded by removing the methods
+ explicitly rather than just clearing the module because it didn't work
+ properly and could be the source of a memory leak.
+
+ *Andrew White*
+
+* Fix a bug in `ActionDispatch::Request#raw_post` that caused `env['rack.input']`
+ to be read but not rewound.
+
+ *Matt Venables*
+
+* More descriptive error messages when calling `render :partial` with
+ an invalid `:layout` argument.
+
+ Fixes #8376.
+
+ render :partial => 'partial', :layout => true
+ # results in ActionView::MissingTemplate: Missing partial /true
+
+ *Yves Senn*
+
+* Accept symbols as `#send_data` :disposition value. [Backport #8329] *Elia Schito*
+
+* Add i18n scope to `distance_of_time_in_words`. [Backport #7997] *Steve Klabnik*
+
+* Fix side effect of `url_for` changing the `:controller` string option. [Backport #6003]
+ Before:
+
+ controller = '/projects'
+ url_for :controller => controller, :action => 'status'
+
+ puts controller #=> 'projects'
+
+ After
+
+ puts controller #=> '/projects'
+
+ *Nikita Beloglazov + Andrew White*
+
+* Introduce `ActionView::Template::Handlers::ERB.escape_whitelist`. This is a list
+ of mime types where template text is not html escaped by default. It prevents `Jack & Joe`
+ from rendering as `Jack &amp; Joe` for the whitelisted mime types. The default whitelist
+ contains text/plain. Fix #7976 [Backport #8235]
+
+ *Joost Baaij*
+
+* `BestStandardsSupport` middleware now appends it's `X-UA-Compatible` value to app's
+ returned value if any. Fix #8086 [Backport #8093]
+
+ *Nikita Afanasenko*
+
+* prevent double slashes in engine urls when `Rails.application.default_url_options[:trailing_slash] = true` is set
+ Fix #7842
+
+ *Yves Senn*
+
+* Fix input name when `:multiple => true` and `:index` are set.
+
+ Before:
+
+ check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1)
+ #=> <input name=\"post[foo][comment_ids]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids]\" type=\"checkbox\" value=\"1\" />
+
+ After:
+
+ check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1)
+ #=> <input name=\"post[foo][comment_ids][]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids][]\" type=\"checkbox\" value=\"1\" />
+
+ Fix #8108
+
+ *Daniel Fox, Grant Hutchins & Trace Wax*
+
+
+## Rails 3.2.12 (Feb 11, 2013) ##
+
+* No changes.
+
+
+## Rails 3.2.11 (Jan 8, 2013) ##
+
+* Strip nils from collections on JSON and XML posts. [CVE-2013-0155]
+
+
+## Rails 3.2.10 (Jan 2, 2013) ##
+
+* No changes.
-## Rails 3.2.10 ##
## Rails 3.2.9 (Nov 12, 2012) ##
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index de3354d4f9..9d1ff8cbe4 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -58,7 +58,8 @@ module ActionController
end
def method_for_action(action_name)
- super || (respond_to?(:method_missing) && "_handle_method_missing")
+ super || ((self.class.public_method_defined?(:method_missing) ||
+ self.class.protected_method_defined?(:method_missing)) && "_handle_method_missing")
end
end
end
diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb
index 62e16ea047..95c058fc86 100644
--- a/actionpack/lib/action_controller/metal/data_streaming.rb
+++ b/actionpack/lib/action_controller/metal/data_streaming.rb
@@ -141,7 +141,7 @@ module ActionController #:nodoc:
raise ArgumentError, ":#{arg} option required" if options[arg].nil?
end
- disposition = options[:disposition]
+ disposition = options[:disposition].to_s
disposition += %(; filename="#{options[:filename]}") if options[:filename]
content_type = options[:type]
diff --git a/actionpack/lib/action_controller/metal/hide_actions.rb b/actionpack/lib/action_controller/metal/hide_actions.rb
index b55c4643be..1ded166491 100644
--- a/actionpack/lib/action_controller/metal/hide_actions.rb
+++ b/actionpack/lib/action_controller/metal/hide_actions.rb
@@ -27,20 +27,14 @@ module ActionController
self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s)).freeze
end
- def inherited(klass)
- klass.class_eval { @visible_actions = {} }
- super
- end
-
def visible_action?(action_name)
- return @visible_actions[action_name] if @visible_actions.key?(action_name)
- @visible_actions[action_name] = !hidden_actions.include?(action_name)
+ not hidden_actions.include?(action_name)
end
# Overrides AbstractController::Base#action_methods to remove any methods
# that are listed as hidden methods.
def action_methods
- @action_methods ||= Set.new(super.reject { |name| hidden_actions.include?(name) })
+ @action_methods ||= Set.new(super.reject { |name| hidden_actions.include?(name) }).freeze
end
end
end
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 9abb86caf8..7630ee2926 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -77,7 +77,7 @@ module ActionController
private
def _extract_redirect_to_status(options, response_status)
- status = if options.is_a?(Hash) && options.key?(:status)
+ if options.is_a?(Hash) && options.key?(:status)
Rack::Utils.status_code(options.delete(:status))
elsif response_status.key?(:status)
Rack::Utils.status_code(response_status[:status])
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index b574a36430..dc58166f78 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -20,7 +20,12 @@ module ActionController
ActiveSupport::Notifications.subscribe("render_template.action_view") do |name, start, finish, id, payload|
path = payload[:layout]
- @layouts[path] += 1
+ if path
+ @layouts[path] += 1
+ if path =~ /^layouts\/(.*)/
+ @layouts[$1] += 1
+ end
+ end
end
ActiveSupport::Notifications.subscribe("!render_template.action_view") do |name, start, finish, id, payload|
@@ -56,6 +61,15 @@ module ActionController
# # assert that the "new" view template was rendered
# assert_template "new"
#
+ # # assert that the layout 'admin' was rendered
+ # assert_template :layout => 'admin'
+ # assert_template :layout => 'layouts/admin'
+ # assert_template :layout => :admin
+ #
+ # # assert that no layout was rendered
+ # assert_template :layout => nil
+ # assert_template :layout => false
+ #
# # assert that the "_customer" partial was rendered twice
# assert_template :partial => '_customer', :count => 2
#
@@ -70,6 +84,8 @@ module ActionController
#
def assert_template(options = {}, message = nil)
validate_request!
+ # Force body to be read in case the template is being streamed
+ response.body
case options
when NilClass, String, Symbol
@@ -86,17 +102,18 @@ module ActionController
end
end
when Hash
- if expected_layout = options[:layout]
+ if options.key?(:layout)
+ expected_layout = options[:layout]
msg = build_message(message,
"expecting layout <?> but action rendered <?>",
expected_layout, @layouts.keys)
case expected_layout
- when String
- assert(@layouts.keys.include?(expected_layout), msg)
+ when String, Symbol
+ assert(@layouts.keys.include?(expected_layout.to_s), msg)
when Regexp
assert(@layouts.keys.any? {|l| l =~ expected_layout }, msg)
- when nil
+ when nil, false
assert(@layouts.empty?, msg)
end
end
@@ -123,7 +140,7 @@ module ActionController
options[:partial], @partials.keys)
assert(@partials.include?(expected_partial), msg)
end
- else
+ elsif options.key?(:partial)
assert @partials.empty?,
"Expected no partials to be rendered"
end
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
index af06bffa16..994e11563d 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
@@ -66,7 +66,7 @@ module HTML
# A regular expression of the valid characters used to separate protocols like
# the ':' in 'http://foo.com'
- self.protocol_separator = /:|(&#0*58)|(&#x70)|(%|&#37;)3A/
+ self.protocol_separator = /:|(&#0*58)|(&#x70)|(&#x0*3a)|(%|&#37;)3A/i
# Specifies a Set of HTML attributes that can have URIs.
self.uri_attributes = Set.new(%w(href src cite action longdesc xlink:href lowsrc))
@@ -110,8 +110,8 @@ module HTML
style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ')
# gauntlet
- if style !~ /^([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*$/ ||
- style !~ /^(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*$/
+ if style !~ /\A([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*\z/ ||
+ style !~ /\A(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*\z/
return ''
end
@@ -122,7 +122,7 @@ module HTML
elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
unless val.split().any? do |keyword|
!allowed_css_keywords.include?(keyword) &&
- keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/
+ keyword !~ /\A(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)\z/
end
clean << prop + ': ' + val + ';'
end
@@ -171,7 +171,7 @@ module HTML
def contains_bad_protocols?(attr_name, value)
uri_attributes.include?(attr_name) &&
- (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(%|&#37;)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first.downcase))
+ (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(&#x0*3a)|(%|&#37;)3A/i && !allowed_protocols.include?(value.split(protocol_separator).first.downcase.strip))
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index 8dd1af7f3d..d5b9e55139 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -26,8 +26,6 @@ module ActionDispatch
module FilterParameters
extend ActiveSupport::Concern
- @@parameter_filter_for = {}
-
# Return a hash of parameters with all sensitive data replaced.
def filtered_parameters
@filtered_parameters ||= parameter_filter.filter(parameters)
@@ -54,7 +52,7 @@ module ActionDispatch
end
def parameter_filter_for(filters)
- @@parameter_filter_for[filters] ||= ParameterFilter.new(filters)
+ ParameterFilter.new(filters)
end
KV_RE = '[^&;=]+'
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 5c48a60469..42f14bc1e9 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -98,8 +98,8 @@ module ActionDispatch
BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
def valid_accept_header
- (xhr? && (accept || content_mime_type)) ||
- (accept && accept !~ BROWSER_LIKE_ACCEPTS)
+ (xhr? && (accept.present? || content_mime_type)) ||
+ (accept.present? && accept !~ BROWSER_LIKE_ACCEPTS)
end
def use_accept_header
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index dea8e86808..31155732d2 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -56,7 +56,12 @@ module ActionDispatch
RFC5789 = %w(PATCH)
HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789
- HTTP_METHOD_LOOKUP = Hash.new { |h, m| h[m] = m.underscore.to_sym if HTTP_METHODS.include?(m) }
+ HTTP_METHOD_LOOKUP = {}
+
+ # Populate the HTTP method lookup cache
+ HTTP_METHODS.each do |method|
+ HTTP_METHOD_LOOKUP[method] = method.underscore.to_sym
+ end
# Returns the HTTP \method that the application should see.
# In the case where the \method was overridden by a middleware
@@ -179,8 +184,9 @@ module ActionDispatch
# work with raw requests directly.
def raw_post
unless @env.include? 'RAW_POST_DATA'
- @env['RAW_POST_DATA'] = body.read(@env['CONTENT_LENGTH'].to_i)
- body.rewind if body.respond_to?(:rewind)
+ raw_post_body = body
+ @env['RAW_POST_DATA'] = raw_post_body.read(@env['CONTENT_LENGTH'].to_i)
+ raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
end
@env['RAW_POST_DATA']
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 64459836b5..f07d5adc9b 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -43,7 +43,11 @@ module ActionDispatch
params = options[:params] || {}
params.reject! {|k,v| v.to_param.nil? }
- rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
+ if options[:trailing_slash] && !path.ends_with?('/')
+ rewritten_url << path.sub(/(\?|\z)/) { "/" + $& }
+ else
+ rewritten_url << path
+ end
rewritten_url << "?#{params.to_query}" unless params.empty?
rewritten_url << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
rewritten_url
diff --git a/actionpack/lib/action_dispatch/middleware/best_standards_support.rb b/actionpack/lib/action_dispatch/middleware/best_standards_support.rb
index 69adcc419f..94efeb79fa 100644
--- a/actionpack/lib/action_dispatch/middleware/best_standards_support.rb
+++ b/actionpack/lib/action_dispatch/middleware/best_standards_support.rb
@@ -15,7 +15,15 @@ module ActionDispatch
def call(env)
status, headers, body = @app.call(env)
- headers["X-UA-Compatible"] = @header
+
+ if headers["X-UA-Compatible"] && @header
+ unless headers["X-UA-Compatible"][@header]
+ headers["X-UA-Compatible"] << "," << @header.to_s
+ end
+ else
+ headers["X-UA-Compatible"] = @header
+ end
+
[status, headers, body]
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index ac726895fa..0e03d85d9a 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -44,10 +44,10 @@ module ActionDispatch
when :yaml
YAML.load(request.raw_post)
when :json
- data = request.deep_munge ActiveSupport::JSON.decode(request.body)
+ data = ActiveSupport::JSON.decode(request.body)
request.body.rewind if request.body.respond_to?(:rewind)
data = {:_json => data} unless data.is_a?(Hash)
- data.with_indifferent_access
+ request.deep_munge(data).with_indifferent_access
else
false
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index 29e9e6c261..80c596fd51 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -22,15 +22,12 @@ module ActionDispatch
#
# Session options:
#
- # * <tt>:secret</tt>: An application-wide key string or block returning a
- # string called per generated digest. The block is called with the
- # CGI::Session instance as an argument. It's important that the secret
- # is not vulnerable to a dictionary attack. Therefore, you should choose
- # a secret consisting of random numbers and letters and more than 30
- # characters. Examples:
+ # * <tt>:secret</tt>: An application-wide key string. It's important that
+ # the secret is not vulnerable to a dictionary attack. Therefore, you
+ # should choose a secret consisting of random numbers and letters and
+ # more than 30 characters.
#
# :secret => '449fe2e7daee471bffae2fd8dc02313d'
- # :secret => Proc.new { User.current_user.secret_key }
#
# * <tt>:digest</tt>: The message digest algorithm used to verify session
# integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index ad11b6a211..a8d176560c 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -5,7 +5,8 @@ module ActionDispatch
def initialize(root, cache_control)
@root = root.chomp('/')
@compiled_root = /^#{Regexp.escape(root)}/
- @file_server = ::Rack::File.new(@root, cache_control)
+ headers = cache_control && { 'Cache-Control' => cache_control }
+ @file_server = ::Rack::File.new(@root, headers)
end
def match?(path)
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 952219631a..d71b21efc3 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -51,7 +51,6 @@ module ActionDispatch
class Mapping #:nodoc:
IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix]
ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
- SHORTHAND_REGEX = %r{/[\w/]+$}
WILDCARD_PATH = %r{\*([^/\)]+)\)?$}
def initialize(set, scope, path, options)
@@ -68,14 +67,7 @@ module ActionDispatch
private
def normalize_options!
- path_without_format = @path.sub(/\(\.:format\)$/, '')
-
- if using_match_shorthand?(path_without_format, @options)
- to_shorthand = @options[:to].blank?
- @options[:to] ||= path_without_format.gsub(/\(.*\)/, "")[1..-1].sub(%r{/([^/]*)$}, '#\1')
- end
-
- @options.merge!(default_controller_and_action(to_shorthand))
+ @options.merge!(default_controller_and_action)
requirements.each do |name, requirement|
# segment_keys.include?(k.to_s) || k == :controller
@@ -153,7 +145,7 @@ module ActionDispatch
end
end
- def default_controller_and_action(to_shorthand=nil)
+ def default_controller_and_action
if to.respond_to?(:call)
{ }
else
@@ -166,7 +158,7 @@ module ActionDispatch
controller ||= default_controller
action ||= default_action
- unless controller.is_a?(Regexp) || to_shorthand
+ unless controller.is_a?(Regexp)
controller = [@scope[:module], controller].compact.join("/").presence
end
@@ -451,7 +443,7 @@ module ActionDispatch
# we must actually delete prefix segment keys to avoid passing them to next url_for
_route.segment_keys.each { |k| options.delete(k) }
prefix = _routes.url_helpers.send("#{name}_path", prefix_options)
- prefix = '' if prefix == '/'
+ prefix = prefix.gsub(%r{/\z}, '')
prefix
end
end
@@ -1261,6 +1253,11 @@ module ActionDispatch
paths = [path] + rest
end
+ path_without_format = path.to_s.sub(/\(\.:format\)$/, '')
+ if using_match_shorthand?(path_without_format, options)
+ options[:to] ||= path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1')
+ end
+
options[:anchor] = true unless options.key?(:anchor)
if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
@@ -1271,6 +1268,10 @@ module ActionDispatch
self
end
+ def using_match_shorthand?(path, options)
+ path && (options[:to] || options[:action]).nil? && path =~ %r{/[\w/]+$}
+ end
+
def decomposed_match(path, options) # :nodoc:
if on = options.delete(:on)
send(on) { decomposed_match(path, options) }
@@ -1288,9 +1289,10 @@ module ActionDispatch
def add_route(action, options) # :nodoc:
path = path_for_action(action, options.delete(:path))
+ action = action.to_s.dup
- if action.to_s =~ /^[\w\/]+$/
- options[:action] ||= action unless action.to_s.include?("/")
+ if action =~ /^[\w\/]+$/
+ options[:action] ||= action unless action.include?("/")
else
action = nil
end
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index dc28389360..50b20a2a25 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -78,10 +78,10 @@ module ActionDispatch
# params, depending of how many arguments your block accepts. A string is required as a
# return value.
#
- # match 'jokes/:number', :to => redirect do |params, request|
- # path = (params[:number].to_i.even? ? "/wheres-the-beef" : "/i-love-lamp")
+ # match 'jokes/:number', :to => redirect { |params, request|
+ # path = (params[:number].to_i.even? ? "wheres-the-beef" : "i-love-lamp")
# "http://#{request.host_with_port}/#{path}"
- # end
+ # }
#
# The options version of redirect allows you to supply only the parts of the url which need
# to change, it also supports interpolation of the path similar to the first example.
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 667094c469..a993699e05 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -94,7 +94,12 @@ module ActionDispatch
attr_reader :routes, :helpers, :module
def initialize
- clear!
+ @routes = {}
+ @helpers = []
+
+ @module = Module.new do
+ instance_methods.each { |selector| remove_method(selector) }
+ end
end
def helper_names
@@ -102,12 +107,14 @@ module ActionDispatch
end
def clear!
+ @helpers.each do |helper|
+ @module.module_eval do
+ remove_possible_method helper
+ end
+ end
+
@routes = {}
@helpers = []
-
- @module ||= Module.new do
- instance_methods.each { |selector| remove_method(selector) }
- end
end
def add(name, route)
@@ -291,7 +298,6 @@ module ActionDispatch
def clear!
@finalized = false
- @url_helpers = nil
named_routes.clear
set.clear
formatter.clear
@@ -442,12 +448,12 @@ module ActionDispatch
normalize_options!
normalize_controller_action_id!
use_relative_controller!
- controller.sub!(%r{^/}, '') if controller
+ normalize_controller!
handle_nil_action!
end
def controller
- @controller ||= @options[:controller]
+ @options[:controller]
end
def current_controller
@@ -504,10 +510,15 @@ module ActionDispatch
old_parts = current_controller.split('/')
size = controller.count("/") + 1
parts = old_parts[0...-size] << controller
- @controller = @options[:controller] = parts.join("/")
+ @options[:controller] = parts.join("/")
end
end
+ # Remove leading slashes from controllers
+ def normalize_controller!
+ @options[:controller] = controller.sub(%r{^/}, '') if controller
+ end
+
# This handles the case of :action => nil being explicitly passed.
# It is identical to :action => "index"
def handle_nil_action!
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index 10832373e1..f319266765 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -2,7 +2,7 @@ module ActionPack
module VERSION #:nodoc:
MAJOR = 3
MINOR = 2
- TINY = 11
+ TINY = 13
PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 87e2332c42..99aa144d3a 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -65,12 +65,17 @@ module ActionView
# distance_of_time_in_words(Time.now, Time.now) # => less than a minute
#
def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {})
+ options = {
+ :scope => :'datetime.distance_in_words',
+ }.merge!(options)
+
from_time = from_time.to_time if from_time.respond_to?(:to_time)
to_time = to_time.to_time if to_time.respond_to?(:to_time)
- distance_in_minutes = (((to_time - from_time).abs)/60).round
- distance_in_seconds = ((to_time - from_time).abs).round
+ distance = (to_time.to_f - from_time.to_f).abs
+ distance_in_minutes = (distance / 60.0).round
+ distance_in_seconds = distance.round
- I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale|
+ I18n.with_options :locale => options[:locale], :scope => options[:scope] do |locale|
case distance_in_minutes
when 0..1
return distance_in_minutes == 0 ?
@@ -129,8 +134,8 @@ module ActionView
# from_time = Time.now - 3.days - 14.minutes - 25.seconds
# time_ago_in_words(from_time) # => 3 days
#
- def time_ago_in_words(from_time, include_seconds = false)
- distance_of_time_in_words(from_time, Time.now, include_seconds)
+ def time_ago_in_words(from_time, include_seconds = false, options = {})
+ distance_of_time_in_words(from_time, Time.now, include_seconds, options)
end
alias_method :distance_of_time_in_words_to_now, :time_ago_in_words
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index a3409ee3c7..0c079256a3 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -331,9 +331,9 @@ module ActionView
# In many cases you will want to wrap the above in another helper, so you
# could do something like the following:
#
- # def labelled_form_for(record_or_name_or_array, *args, &proc)
+ # def labelled_form_for(record_or_name_or_array, *args, &block)
# options = args.extract_options!
- # form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &proc)
+ # form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &block)
# end
#
# If you don't need to attach a form to a model instance, then check out
@@ -355,7 +355,7 @@ module ActionView
# <%= form_for @invoice, :url => external_url, :authenticity_token => false do |f|
# ...
# <% end %>
- def form_for(record, options = {}, &proc)
+ def form_for(record, options = {}, &block)
raise ArgumentError, "Missing block" unless block_given?
options[:html] ||= {}
@@ -374,12 +374,10 @@ module ActionView
options[:html][:method] = options.delete(:method) if options.has_key?(:method)
options[:html][:authenticity_token] = options.delete(:authenticity_token)
- builder = options[:parent_builder] = instantiate_builder(object_name, object, options, &proc)
- fields_for = fields_for(object_name, object, options, &proc)
+ builder = options[:parent_builder] = instantiate_builder(object_name, object, options, &block)
+ output = capture(builder, &block)
default_options = builder.multipart? ? { :multipart => true } : {}
- output = form_tag(options.delete(:url) || {}, default_options.merge!(options.delete(:html)))
- output << fields_for
- output.safe_concat('</form>')
+ form_tag(options.delete(:url) || {}, default_options.merge!(options.delete(:html))) { output }
end
def apply_form_for_options!(object_or_array, options) #:nodoc:
@@ -1198,25 +1196,26 @@ module ActionView
def add_default_name_and_id(options)
if options.has_key?("index")
- options["name"] ||= tag_name_with_index(options["index"])
+ options["name"] ||= tag_name_with_index(options["index"], options["multiple"])
options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) }
options.delete("index")
elsif defined?(@auto_index)
- options["name"] ||= tag_name_with_index(@auto_index)
+ options["name"] ||= tag_name_with_index(@auto_index, options["multiple"])
options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) }
else
- options["name"] ||= tag_name + (options['multiple'] ? '[]' : '')
+ options["name"] ||= tag_name(options["multiple"])
options["id"] = options.fetch("id"){ tag_id }
end
+
options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence
end
- def tag_name
- "#{@object_name}[#{sanitized_method_name}]"
+ def tag_name(multiple = false)
+ "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
end
- def tag_name_with_index(index)
- "#{@object_name}[#{index}][#{sanitized_method_name}]"
+ def tag_name_with_index(index, multiple = false)
+ "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
end
def tag_id
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 623c45fa13..f63a951c33 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -508,9 +508,9 @@ module ActionView
convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } }
if priority_zones
- if priority_zones.is_a?(Regexp)
+ if priority_zones.is_a?(Regexp)
priority_zones = model.all.find_all {|z| z =~ priority_zones}
- end
+ end
zone_options += options_for_select(convert_zones[priority_zones], selected)
zone_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 670ff18a66..7268a9ff9a 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -45,7 +45,7 @@ module ActionView
# # => <form action="/posts" method="post">
#
# form_tag('/posts/1', :method => :put)
- # # => <form action="/posts/1" method="put">
+ # # => <form action="/posts/1" method="post"> ... <input name="_method" type="hidden" value="put" /> ...
#
# form_tag('/upload', :multipart => true)
# # => <form action="/upload" method="post" enctype="multipart/form-data">
@@ -53,7 +53,7 @@ module ActionView
# <%= form_tag('/posts') do -%>
# <div><%= submit_tag 'Save' %></div>
# <% end -%>
- # # => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
+ # # => <form action="/posts" method="post"><div><input type="submit" name="commit" value="Save" /></div></form>
#
# <%= form_tag('/posts', :remote => true) %>
# # => <form action="/posts" method="post" data-remote="true">
@@ -181,7 +181,7 @@ module ActionView
# # => <label for="name">Name</label>
#
# label_tag 'name', 'Your name'
- # # => <label for="name">Your Name</label>
+ # # => <label for="name">Your name</label>
#
# label_tag 'name', nil, :class => 'small_label'
# # => <label for="name" class="small_label">Name</label>
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 2f372bd111..ad86d13456 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -1,5 +1,7 @@
# encoding: utf-8
+require 'active_support/core_ext/hash/keys'
+require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/core_ext/big_decimal/conversions'
require 'active_support/core_ext/float/rounding'
require 'active_support/core_ext/object/blank'
@@ -358,7 +360,7 @@ module ActionView
end
- STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze
+ STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
# Formats the bytes in +number+ into a more understandable
# representation (e.g., giving it 1500 yields 1.5 KB). This
@@ -450,7 +452,7 @@ module ActionView
end
DECIMAL_UNITS = {0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
- -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto}.freeze
+ -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto}
# Pretty prints (formats and approximates) a number in a way it
# is more readable by humans (eg.: 1200000000 becomes "1.2
@@ -591,7 +593,7 @@ module ActionView
unit = case units
when Hash
- units[DECIMAL_UNITS[display_exponent]]
+ units[DECIMAL_UNITS[display_exponent]] || ''
when String, Symbol
I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
else
diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb
index aae6389445..709b5d2c17 100644
--- a/actionpack/lib/action_view/helpers/record_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb
@@ -98,7 +98,9 @@ module ActionView
options, prefix = prefix, nil if prefix.is_a?(Hash)
options = options ? options.dup : {}
options.merge!(:class => "#{dom_class(record, prefix)} #{options[:class]}".strip, :id => dom_id(record, prefix))
- if block.arity == 0
+ if !block_given?
+ content_tag(tag_name, "", options)
+ elsif block.arity == 0
content_tag(tag_name, capture(&block), options)
else
content_tag(tag_name, capture(record, &block), options)
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index 33b508e9b5..1a656ed37f 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -44,7 +44,13 @@ module ActionView
module Accessors #:nodoc:
end
- register_detail(:locale) { [I18n.locale, I18n.default_locale].uniq }
+ register_detail(:locale) do
+ locales = [I18n.locale]
+ locales.concat(I18n.fallbacks[I18n.locale]) if I18n.respond_to? :fallbacks
+ locales << I18n.default_locale
+ locales.uniq!
+ locales
+ end
register_detail(:formats) { Mime::SET.symbols }
register_detail(:handlers){ Template::Handlers.extensions }
@@ -97,7 +103,7 @@ module ActionView
# Helpers related to template lookup using the lookup context information.
module ViewPaths
- attr_reader :view_paths
+ attr_reader :view_paths, :html_fallback_for_js
# Whenever setting view paths, makes a copy so we can manipulate then in
# instance objects as we wish.
@@ -194,7 +200,10 @@ module ActionView
def formats=(values)
if values
values.concat(default_formats) if values.delete "*/*"
- values << :html if values == [:js]
+ if values == [:js]
+ values << :html
+ @html_fallback_for_js = true
+ end
end
super(values)
end
diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb
index 0b5d3785d4..b79b89e142 100644
--- a/actionpack/lib/action_view/renderer/abstract_renderer.rb
+++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb
@@ -37,5 +37,11 @@ module ActionView
def instrument(name, options={})
ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
end
+
+ def prepend_formats(formats)
+ formats = Array(formats)
+ return if formats.empty? || @lookup_context.html_fallback_for_js
+ @lookup_context.formats = formats | @lookup_context.formats
+ end
end
end
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 3093aca91e..f3300e470b 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -256,7 +256,7 @@ module ActionView
object, as = @object, @variable
if !block && (layout = @options[:layout])
- layout = find_template(layout)
+ layout = find_template(layout.to_s)
end
object ||= locals[as]
@@ -281,6 +281,8 @@ module ActionView
@block = block
@details = extract_details(options)
+ prepend_formats(options[:formats])
+
if String === partial
@object = options[:object]
@path = partial
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index a27d5dd1b1..d15e75637a 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -10,9 +10,10 @@ module ActionView
template = determine_template(options)
context = @lookup_context
+ prepend_formats(template.formats)
+
unless context.rendered_format
- context.formats = template.formats unless template.formats.empty?
- context.rendered_format = context.formats.first
+ context.rendered_format = template.formats.first || formats.last
end
render_template(template, options[:layout], options[:locals])
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index ea495ea9ca..118b18a81c 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -15,6 +15,17 @@ module ActionView
src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
end
+ # Erubis toggles <%= and <%== behavior when escaping is enabled.
+ # We override to always treat <%== as escaped.
+ def add_expr(src, code, indicator)
+ case indicator
+ when '=='
+ add_expr_escaped(src, code)
+ else
+ super
+ end
+ end
+
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
def add_expr_literal(src, code)
@@ -48,6 +59,10 @@ module ActionView
class_attribute :erb_implementation
self.erb_implementation = Erubis
+ # Do not escape templates of these mime types.
+ class_attribute :escape_whitelist
+ self.escape_whitelist = ["text/plain"]
+
ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
def self.call(template)
@@ -83,6 +98,7 @@ module ActionView
self.class.erb_implementation.new(
erb,
+ :escape => (self.class.escape_whitelist.include? template.mime_type),
:trim => (self.class.erb_trim_mode == "-")
).src
end
diff --git a/actionpack/lib/sprockets/helpers/rails_helper.rb b/actionpack/lib/sprockets/helpers/rails_helper.rb
index 690c71b472..243c2e5e50 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.uniq.join("\n").html_safe
+ end.flatten.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.uniq.join("\n").html_safe
+ end.flatten.uniq.join("\n").html_safe
end
def asset_path(source, options = {})
@@ -157,18 +157,25 @@ module Sprockets
end
def rewrite_extension(source, dir, ext)
- source_ext = File.extname(source)
- if ext && source_ext != ".#{ext}"
- if !source_ext.empty? && (asset = asset_environment[source]) &&
- asset.pathname.to_s =~ /#{source}\Z/
- source
- else
- "#{source}.#{ext}"
- end
- else
+ source_ext = File.extname(source)[1..-1]
+
+ if !ext || ext == source_ext
+ source
+ elsif source_ext.blank?
+ "#{source}.#{ext}"
+ elsif File.exists?(source) || exact_match_present?(source)
source
+ else
+ "#{source}.#{ext}"
end
end
+
+ def exact_match_present?(source)
+ pathname = asset_environment.resolve(source)
+ pathname.to_s =~ /#{Regexp.escape(source)}\Z/
+ rescue Sprockets::FileNotFound
+ false
+ end
end
end
end
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index 5252e43c25..3b5a515e84 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -76,6 +76,11 @@ class ActionPackAssertionsController < ActionController::Base
render "test/hello_world", :layout => "layouts/standard"
end
+ def render_with_layout_and_partial
+ @variable_for_layout = nil
+ render "test/hello_world_with_partial", :layout => "layouts/standard"
+ end
+
def session_stuffing
session['xmas'] = 'turkey'
render :text => "ho ho ho"
@@ -483,11 +488,43 @@ class AssertTemplateTest < ActionController::TestCase
end
end
+ def test_fails_expecting_no_layout
+ get :render_with_layout
+ assert_raise(ActiveSupport::TestCase::Assertion) do
+ assert_template :layout => nil
+ end
+ end
+
def test_passes_with_correct_layout
get :render_with_layout
assert_template :layout => "layouts/standard"
end
+ def test_passes_with_layout_and_partial
+ get :render_with_layout_and_partial
+ assert_template :layout => "layouts/standard"
+ end
+
+ def test_passed_with_no_layout
+ get :hello_world
+ assert_template :layout => nil
+ end
+
+ def test_passed_with_no_layout_false
+ get :hello_world
+ assert_template :layout => false
+ end
+
+ def test_passes_with_correct_layout_without_layouts_prefix
+ get :render_with_layout
+ assert_template :layout => "standard"
+ end
+
+ def test_passes_with_correct_layout_symbol
+ get :render_with_layout
+ assert_template :layout => :standard
+ end
+
def test_assert_template_reset_between_requests
get :hello_world
assert_template 'test/hello_world'
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index affa9a6add..a652d8ffad 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -86,6 +86,12 @@ end
class RecordIdentifierController < ActionController::Base
end
+class ActionMissingController < ActionController::Base
+ def action_missing(action)
+ render :text => "Response for #{action}"
+ end
+end
+
class ControllerClassTests < ActiveSupport::TestCase
def test_controller_path
@@ -196,6 +202,12 @@ class PerformActionTest < ActionController::TestCase
assert_raise(AbstractController::ActionNotFound) { get :hidden_action }
assert_raise(AbstractController::ActionNotFound) { get :another_hidden_action }
end
+
+ def test_action_missing_should_work
+ use_controller ActionMissingController
+ get :arbitrary_action
+ assert_equal "Response for arbitrary_action", @response.body
+ end
end
class UrlOptionsTest < ActionController::TestCase
diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb
index bc171e201b..d8b04119f2 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -78,6 +78,14 @@ end
class DefaultLayoutController < LayoutTest
end
+class StreamingLayoutController < LayoutTest
+ def render(*args)
+ options = args.extract_options!
+ options[:stream] = true
+ super(*(args << options))
+ end
+end
+
class AbsolutePathLayoutController < LayoutTest
layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/layouts/layout_test')
end
@@ -122,6 +130,14 @@ class LayoutSetInResponseTest < ActionController::TestCase
assert_template :layout => "layouts/layout_test"
end
+ if RUBY_VERSION >= '1.9'
+ def test_layout_set_when_using_streaming_layout
+ @controller = StreamingLayoutController.new
+ get :hello
+ assert_template :hello
+ end
+ end
+
def test_layout_set_when_set_in_controller
@controller = HasOwnLayoutController.new
get :hello
diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb
index 41ff2f3809..a5fc3f614a 100644
--- a/actionpack/test/controller/localized_templates_test.rb
+++ b/actionpack/test/controller/localized_templates_test.rb
@@ -19,4 +19,13 @@ class LocalizedTemplatesTest < ActionController::TestCase
get :hello_world
assert_equal "Hello World", @response.body
end
+
+ def test_use_fallback_locales
+ I18n.locale = :"de-AT"
+ I18n.backend.class.send(:include, I18n::Backend::Fallbacks)
+ I18n.fallbacks[:"de-AT"] = [:de]
+
+ get :hello_world
+ assert_equal "Gutten Tag", @response.body
+ end
end \ No newline at end of file
diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb
index 29c8885d9f..c727f03346 100644
--- a/actionpack/test/controller/new_base/render_template_test.rb
+++ b/actionpack/test/controller/new_base/render_template_test.rb
@@ -9,7 +9,8 @@ module RenderTemplate
"locals.html.erb" => "The secret is <%= secret %>",
"xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend",
"with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>",
- "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %>",
+ "with_implicit_raw.html.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a html template",
+ "with_implicit_raw.text.erb" => "Hello <%== '<strong>this is also raw</strong>' %> in a text template",
"test/with_json.html.erb" => "<%= render :template => 'test/with_json', :formats => [:json] %>",
"test/with_json.json.erb" => "<%= render :template => 'test/final', :formats => [:json] %>",
"test/final.json.erb" => "{ final: json }",
@@ -113,7 +114,12 @@ module RenderTemplate
get :with_implicit_raw
- assert_body "Hello <strong>this is also raw</strong>"
+ assert_body "Hello <strong>this is also raw</strong> in a html template"
+ assert_status 200
+
+ get :with_implicit_raw, :format => 'text'
+
+ assert_body "Hello <strong>this is also raw</strong> in a text template"
assert_status 200
end
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 8c631e218f..3964540def 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -1406,10 +1406,11 @@ class RenderTest < ActionController::TestCase
end
def test_locals_option_to_assert_template_is_not_supported
+ get :partial_collection_with_locals
+
warning_buffer = StringIO.new
$stderr = warning_buffer
- get :partial_collection_with_locals
assert_template :partial => 'customer_greeting', :locals => { :greeting => 'Bonjour' }
assert_equal "the :locals option to #assert_template is only supported in a ActionView::TestCase\n", warning_buffer.string
ensure
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 7079a6ff90..1a1610eac7 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -58,13 +58,13 @@ class UriReservedCharactersRoutingTest < Test::Unit::TestCase
end
class MockController
- def self.build(helpers)
+ def self.build(helpers, additional_options = {})
Class.new do
- def url_for(options)
+ define_method :url_for do |options|
options[:protocol] ||= "http"
options[:host] ||= "test.host"
- super(options)
+ super(options.merge(additional_options))
end
include helpers
@@ -468,8 +468,8 @@ class LegacyRouteSetTests < Test::Unit::TestCase
routes.send(:pages_url)
end
- def setup_for_named_route
- MockController.build(rs.url_helpers).new
+ def setup_for_named_route(options = {})
+ MockController.build(rs.url_helpers, options).new
end
def test_named_route_without_hash
@@ -487,6 +487,16 @@ class LegacyRouteSetTests < Test::Unit::TestCase
assert_equal("/", routes.send(:root_path))
end
+ def test_named_route_root_with_trailing_slash
+ rs.draw do
+ root :to => "hello#index"
+ end
+
+ routes = setup_for_named_route(:trailing_slash => true)
+ assert_equal("http://test.host/", routes.send(:root_url))
+ assert_equal("http://test.host/?foo=bar", routes.send(:root_url, :foo => :bar))
+ end
+
def test_named_route_with_regexps
rs.draw do
match 'page/:year/:month/:day/:title' => 'page#show', :as => 'article',
diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb
index 8f885ff28e..4e4dd8d005 100644
--- a/actionpack/test/controller/send_file_test.rb
+++ b/actionpack/test/controller/send_file_test.rb
@@ -118,6 +118,18 @@ class SendFileTest < ActionController::TestCase
assert_equal 'private', h['Cache-Control']
end
+ def test_send_file_headers_with_disposition_as_a_symbol
+ options = {
+ :type => Mime::PNG,
+ :disposition => :disposition,
+ :filename => 'filename'
+ }
+
+ @controller.headers = {}
+ @controller.send(:send_file_headers!, options)
+ assert_equal 'disposition; filename="filename"', @controller.headers['Content-Disposition']
+ end
+
def test_send_file_headers_with_mime_lookup_with_symbol
options = {
:type => :png
@@ -138,7 +150,7 @@ class SendFileTest < ActionController::TestCase
@controller.headers = {}
assert_raise(ArgumentError){ @controller.send(:send_file_headers!, options) }
end
-
+
def test_send_file_headers_guess_type_from_extension
{
'image.png' => 'image/png',
diff --git a/actionpack/test/dispatch/best_standards_support_test.rb b/actionpack/test/dispatch/best_standards_support_test.rb
new file mode 100644
index 0000000000..551bb9621a
--- /dev/null
+++ b/actionpack/test/dispatch/best_standards_support_test.rb
@@ -0,0 +1,35 @@
+require 'abstract_unit'
+
+class BestStandardsSupportTest < ActiveSupport::TestCase
+ def test_with_best_standards_support
+ _, headers, _ = app(true, {}).call({})
+ assert_equal "IE=Edge,chrome=1", headers["X-UA-Compatible"]
+ end
+
+ def test_with_builtin_best_standards_support
+ _, headers, _ = app(:builtin, {}).call({})
+ assert_equal "IE=Edge", headers["X-UA-Compatible"]
+ end
+
+ def test_without_best_standards_support
+ _, headers, _ = app(false, {}).call({})
+ assert_equal nil, headers["X-UA-Compatible"]
+ end
+
+ def test_appends_to_app_headers_without_duplication_after_multiple_requests
+ app_headers = { "X-UA-Compatible" => "requiresActiveX=true" }
+ _, headers, _ = app(true, app_headers).call({})
+ _, headers, _ = app(true, app_headers).call({})
+
+ expects = "requiresActiveX=true,IE=Edge,chrome=1"
+ assert_equal expects, headers["X-UA-Compatible"]
+ end
+
+ private
+
+ def app(type, headers)
+ app = proc { [200, headers, "response"] }
+ ActionDispatch::BestStandardsSupport.new(app, type)
+ end
+
+end
diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb
index bd5b5edab0..88dc2c093b 100644
--- a/actionpack/test/dispatch/prefix_generation_test.rb
+++ b/actionpack/test/dispatch/prefix_generation_test.rb
@@ -248,6 +248,11 @@ module TestGenerationPrefix
assert_equal "/something/", app_object.root_path
end
+ test "[OBJECT] generating application's route includes default_url_options[:trailing_slash]" do
+ RailsApplication.routes.default_url_options[:trailing_slash] = true
+ assert_equal "/awesome/blog/posts", engine_object.posts_path
+ end
+
test "[OBJECT] generating engine's route with url_for" do
path = engine_object.url_for(:controller => "inside_engine_generating",
:action => "show",
diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb
index fbf2ce1fbe..4886bf13b2 100644
--- a/actionpack/test/dispatch/request/json_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb
@@ -120,6 +120,13 @@ class RootLessJSONParamsParsingTest < ActionDispatch::IntegrationTest
)
end
+ test "parses json with non-object JSON content" do
+ assert_parses(
+ {"user" => {"_json" => "string content" }, "_json" => "string content" },
+ "\"string content\"", { 'CONTENT_TYPE' => 'application/json' }
+ )
+ end
+
private
def assert_parses(expected, actual, headers = {})
with_test_routing(UsersController) do
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 5b3d38c48c..222cdfde1f 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -371,6 +371,27 @@ class RequestTest < ActiveSupport::TestCase
assert request.put?
end
+ test "post uneffected by local inflections" do
+ existing_acrnoyms = ActiveSupport::Inflector.inflections.acronyms.dup
+ existing_acrnoym_regex = ActiveSupport::Inflector.inflections.acronym_regex.dup
+ begin
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.acronym "POS"
+ end
+ assert_equal "pos_t", "POST".underscore
+ request = stub_request "REQUEST_METHOD" => "POST"
+ assert_equal :post, ActionDispatch::Request::HTTP_METHOD_LOOKUP["POST"]
+ assert_equal :post, request.method_symbol
+ assert request.post?
+ ensure
+ # Reset original acronym set
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.send(:instance_variable_set,"@acronyms",existing_acrnoyms)
+ inflect.send(:instance_variable_set,"@acronym_regex",existing_acrnoym_regex)
+ end
+ end
+ end
+
test "xml format" do
request = stub_request
request.expects(:parameters).at_least_once.returns({ :format => 'xml' })
@@ -460,6 +481,15 @@ class RequestTest < ActiveSupport::TestCase
request.expects(:parameters).at_least_once.returns({})
assert_equal [ Mime::HTML ], request.formats
+ request = stub_request 'HTTP_ACCEPT' => ''
+ request.expects(:parameters).at_least_once.returns({})
+ assert_equal [Mime::HTML], request.formats
+
+ request = stub_request 'HTTP_ACCEPT' => '',
+ 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"
+ request.expects(:parameters).at_least_once.returns({})
+ assert_equal [Mime::JS], request.formats
+
request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8',
'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"
request.expects(:parameters).at_least_once.returns({})
@@ -530,6 +560,13 @@ class RequestTest < ActiveSupport::TestCase
assert_equal Mime::XML, request.negotiate_mime([Mime::XML, Mime::CSV])
end
+ test "raw_post rewinds rack.input if RAW_POST_DATA is nil" do
+ request = stub_request('rack.input' => StringIO.new("foo"),
+ 'CONTENT_LENGTH' => 3)
+ assert_equal "foo", request.raw_post
+ assert_equal "foo", request.env['rack.input'].read
+ end
+
test "process parameter filter" do
test_hashes = [
[{'foo'=>'bar'},{'foo'=>'bar'},%w'food'],
diff --git a/actionpack/test/dispatch/routing/route_set_test.rb b/actionpack/test/dispatch/routing/route_set_test.rb
new file mode 100644
index 0000000000..629b8ed8bd
--- /dev/null
+++ b/actionpack/test/dispatch/routing/route_set_test.rb
@@ -0,0 +1,86 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Routing
+ class RouteSetTest < ActiveSupport::TestCase
+ class SimpleApp
+ def initialize(response)
+ @response = response
+ end
+
+ def call(env)
+ [ 200, { 'Content-Type' => 'text/plain' }, [response] ]
+ end
+ end
+
+ setup do
+ @set = RouteSet.new
+ end
+
+ test "url helpers are added when route is added" do
+ draw do
+ get 'foo', :to => SimpleApp.new('foo#index')
+ end
+
+ assert_equal '/foo', url_helpers.foo_path
+ assert_raises NoMethodError do
+ assert_equal '/bar', url_helpers.bar_path
+ end
+
+ draw do
+ get 'foo', :to => SimpleApp.new('foo#index')
+ get 'bar', :to => SimpleApp.new('bar#index')
+ end
+
+ assert_equal '/foo', url_helpers.foo_path
+ assert_equal '/bar', url_helpers.bar_path
+ end
+
+ test "url helpers are updated when route is updated" do
+ draw do
+ get 'bar', :to => SimpleApp.new('bar#index'), :as => :bar
+ end
+
+ assert_equal '/bar', url_helpers.bar_path
+
+ draw do
+ get 'baz', :to => SimpleApp.new('baz#index'), :as => :bar
+ end
+
+ assert_equal '/baz', url_helpers.bar_path
+ end
+
+ test "url helpers are removed when route is removed" do
+ draw do
+ get 'foo', :to => SimpleApp.new('foo#index')
+ get 'bar', :to => SimpleApp.new('bar#index')
+ end
+
+ assert_equal '/foo', url_helpers.foo_path
+ assert_equal '/bar', url_helpers.bar_path
+
+ draw do
+ get 'foo', :to => SimpleApp.new('foo#index')
+ end
+
+ assert_equal '/foo', url_helpers.foo_path
+ assert_raises NoMethodError do
+ assert_equal '/bar', url_helpers.bar_path
+ end
+ end
+
+ private
+ def clear!
+ @set.clear!
+ end
+
+ def draw(&block)
+ @set.draw(&block)
+ end
+
+ def url_helpers
+ @set.url_helpers
+ end
+ end
+ end
+end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index e61b4e2d8f..88a5c37c43 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -515,6 +515,20 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
match '/sculptors', :to => 'italians#sculptors'
match '/painters/:painter', :to => 'italians#painters', :constraints => {:painter => /michelangelo/}
end
+
+ get 'search' => 'search'
+
+ scope ':locale' do
+ match 'questions/new', :via => :get
+ end
+
+ namespace :api do
+ namespace :v3 do
+ scope ':locale' do
+ get "products/list"
+ end
+ end
+ end
end
end
@@ -887,6 +901,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal original_options, options
end
+ def test_url_for_does_not_modify_controller
+ controller = '/projects'
+ options = {:controller => controller, :action => 'status', :only_path => true}
+ url = url_for(options)
+
+ assert_equal '/projects/status', url
+ assert_equal '/projects', controller
+ end
+
# tests the arguments modification free version of define_hash_access
def test_named_route_with_no_side_effects
original_options = { :host => 'test.host' }
@@ -1406,6 +1429,21 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_match_shorthand_inside_scope_with_variables_with_controller
+ with_test_routes do
+ get '/de/questions/new'
+ assert_equal 'questions#new', @response.body
+ assert_equal 'de', @request.params[:locale]
+ end
+ end
+
+ def test_match_shorthand_inside_nested_namespaces_and_scopes_with_controller
+ with_test_routes do
+ get '/api/v3/en/products/list'
+ assert_equal 'api/v3/products#list', @response.body
+ end
+ end
+
def test_dynamically_generated_helpers_on_collection_do_not_clobber_resources_url_helper
with_test_routes do
assert_equal '/replies', replies_path
@@ -2468,6 +2506,11 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal "/posts/1/admin", post_admin_root_path(:post_id => '1')
end
+ def test_action_from_path_is_not_frozen
+ get '/search'
+ assert !@request.params[:action].frozen?
+ end
+
private
def with_test_routes
yield
diff --git a/actionpack/test/fixtures/sprockets/app/javascripts/extra.js b/actionpack/test/fixtures/sprockets/app/javascripts/extra.js
index e69de29bb2..e611d2b129 100644
--- a/actionpack/test/fixtures/sprockets/app/javascripts/extra.js
+++ b/actionpack/test/fixtures/sprockets/app/javascripts/extra.js
@@ -0,0 +1 @@
+//= require xmlhr
diff --git a/actionpack/test/fixtures/sprockets/app/stylesheets/extra.css b/actionpack/test/fixtures/sprockets/app/stylesheets/extra.css
index e69de29bb2..2365eaa4cd 100644
--- a/actionpack/test/fixtures/sprockets/app/stylesheets/extra.css
+++ b/actionpack/test/fixtures/sprockets/app/stylesheets/extra.css
@@ -0,0 +1 @@
+/*= require style */
diff --git a/actionpack/test/fixtures/test/_changing_priority.html.erb b/actionpack/test/fixtures/test/_changing_priority.html.erb
new file mode 100644
index 0000000000..3225efc49a
--- /dev/null
+++ b/actionpack/test/fixtures/test/_changing_priority.html.erb
@@ -0,0 +1 @@
+HTML \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_changing_priority.json.erb b/actionpack/test/fixtures/test/_changing_priority.json.erb
new file mode 100644
index 0000000000..7fa41dce66
--- /dev/null
+++ b/actionpack/test/fixtures/test/_changing_priority.json.erb
@@ -0,0 +1 @@
+JSON \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_first_json_partial.json.erb b/actionpack/test/fixtures/test/_first_json_partial.json.erb
new file mode 100644
index 0000000000..790ee896db
--- /dev/null
+++ b/actionpack/test/fixtures/test/_first_json_partial.json.erb
@@ -0,0 +1 @@
+<%= render :partial => "test/second_json_partial" %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_json_change_priority.json.erb b/actionpack/test/fixtures/test/_json_change_priority.json.erb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/test/_json_change_priority.json.erb
diff --git a/actionpack/test/fixtures/test/_second_json_partial.json.erb b/actionpack/test/fixtures/test/_second_json_partial.json.erb
new file mode 100644
index 0000000000..5ebb7f1afd
--- /dev/null
+++ b/actionpack/test/fixtures/test/_second_json_partial.json.erb
@@ -0,0 +1 @@
+Third level \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/change_priority.html.erb b/actionpack/test/fixtures/test/change_priority.html.erb
new file mode 100644
index 0000000000..71ecef11c4
--- /dev/null
+++ b/actionpack/test/fixtures/test/change_priority.html.erb
@@ -0,0 +1,2 @@
+<%= render :partial => "test/json_change_priority", :formats => :json %>
+HTML Template, but <%= render :partial => "test/changing_priority" %> partial \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/hello_world_with_partial.html.erb b/actionpack/test/fixtures/test/hello_world_with_partial.html.erb
new file mode 100644
index 0000000000..ec31545356
--- /dev/null
+++ b/actionpack/test/fixtures/test/hello_world_with_partial.html.erb
@@ -0,0 +1,2 @@
+Hello world!
+<%= render '/test/partial' %>
diff --git a/actionpack/test/fixtures/test/html_template.html.erb b/actionpack/test/fixtures/test/html_template.html.erb
new file mode 100644
index 0000000000..1b483357bd
--- /dev/null
+++ b/actionpack/test/fixtures/test/html_template.html.erb
@@ -0,0 +1 @@
+<%= render :partial => "test/first_json_partial", :formats => :json %> \ No newline at end of file
diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb
index d45215acfd..b0b407b473 100644
--- a/actionpack/test/template/date_helper_i18n_test.rb
+++ b/actionpack/test/template/date_helper_i18n_test.rb
@@ -36,16 +36,25 @@ class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase
end
end
- def assert_distance_of_time_in_words_translates_key(passed, expected)
+ def test_distance_of_time_in_words_calls_i18n_with_custom_scope
+ {
+ [30.days, false] => [:'about_x_months', 1],
+ [60.days, false] => [:'x_months', 2],
+ }.each do |passed, expected|
+ assert_distance_of_time_in_words_translates_key(passed, expected, {:scope => :'datetime.distance_in_words_ago'})
+ end
+ end
+
+ def assert_distance_of_time_in_words_translates_key(passed, expected, options = {})
diff, include_seconds = *passed
key, count = *expected
to = @from + diff
- options = {:locale => 'en', :scope => :'datetime.distance_in_words'}
+ options = {:locale => 'en', :scope => :'datetime.distance_in_words'}.merge(options)
options[:count] = count if count
I18n.expects(:t).with(key, options)
- distance_of_time_in_words(@from, to, include_seconds, :locale => 'en')
+ distance_of_time_in_words(@from, to, include_seconds, options)
end
def test_distance_of_time_pluralizations
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index cf707e1de5..e4f84f8dd7 100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -128,12 +128,39 @@ class DateHelperTest < ActionView::TestCase
end
def test_distance_in_words_with_integers
- assert_equal "less than a minute", distance_of_time_in_words(59)
+ assert_equal "1 minute", distance_of_time_in_words(59)
assert_equal "about 1 hour", distance_of_time_in_words(60*60)
- assert_equal "less than a minute", distance_of_time_in_words(0, 59)
+ assert_equal "1 minute", distance_of_time_in_words(0, 59)
assert_equal "about 1 hour", distance_of_time_in_words(60*60, 0)
end
+ def test_distance_in_words_with_times
+ assert_equal "1 minute", distance_of_time_in_words(30.seconds)
+ assert_equal "1 minute", distance_of_time_in_words(59.seconds)
+ assert_equal "2 minutes", distance_of_time_in_words(119.seconds)
+ assert_equal "2 minutes", distance_of_time_in_words(1.minute + 59.seconds)
+ assert_equal "3 minutes", distance_of_time_in_words(2.minute + 30.seconds)
+ assert_equal "44 minutes", distance_of_time_in_words(44.minutes + 29.seconds)
+ assert_equal "about 1 hour", distance_of_time_in_words(44.minutes + 30.seconds)
+ assert_equal "about 1 hour", distance_of_time_in_words(60.minutes)
+
+ # include seconds
+ assert_equal "half a minute", distance_of_time_in_words(39.seconds, 0, true)
+ assert_equal "less than a minute", distance_of_time_in_words(40.seconds, 0, true)
+ assert_equal "less than a minute", distance_of_time_in_words(59.seconds, 0, true)
+ assert_equal "1 minute", distance_of_time_in_words(60.seconds, 0, true)
+ end
+
+ def test_distance_in_words_with_offset_datetimes
+ start_date = DateTime.new 1975, 1, 31, 0, 0, 0, '+6'
+ end_date = DateTime.new 1977, 1, 31, 0, 0, 0, '+6'
+ assert_equal("about 2 years", distance_of_time_in_words(start_date, end_date))
+
+ start_date = DateTime.new 1982, 12, 3, 0, 0, 0, '+6'
+ end_date = DateTime.new 2010, 11, 30, 0, 0, 0, '+6'
+ assert_equal("almost 28 years", distance_of_time_in_words(start_date, end_date))
+ end
+
def test_time_ago_in_words
assert_equal "about 1 year", time_ago_in_words(1.year.ago - 1.day)
end
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 19af01e2c8..22af39add4 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -301,6 +301,16 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, file_field("user", "avatar")
end
+ def test_file_field_with_multiple_behavior
+ expected = '<input id="import_file" multiple="multiple" name="import[file][]" type="file" />'
+ assert_dom_equal expected, file_field("import", "file", :multiple => true)
+ end
+
+ def test_file_field_with_multiple_behavior_and_explicit_name
+ expected = '<input id="import_file" multiple="multiple" name="custom" type="file" />'
+ assert_dom_equal expected, file_field("import", "file", :multiple => true, :name => "custom")
+ end
+
def test_hidden_field
assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="Hello World" />',
hidden_field("post", "title")
@@ -393,6 +403,19 @@ class FormHelperTest < ActionView::TestCase
)
end
+ def test_check_box_with_multiple_behavior_and_index
+ @post.comment_ids = [2,3]
+ assert_dom_equal(
+ '<input name="post[foo][comment_ids][]" type="hidden" value="0" /><input id="post_foo_comment_ids_1" name="post[foo][comment_ids][]" type="checkbox" value="1" />',
+ check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1)
+ )
+ assert_dom_equal(
+ '<input name="post[bar][comment_ids][]" type="hidden" value="0" /><input checked="checked" id="post_bar_comment_ids_3" name="post[bar][comment_ids][]" type="checkbox" value="3" />',
+ check_box("post", "comment_ids", { :multiple => true, :index => "bar" }, 3)
+ )
+
+ end
+
def test_checkbox_disabled_disables_hidden_field
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" disabled="disabled"/><input checked="checked" disabled="disabled" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
@@ -742,7 +765,6 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
-
def test_form_for_with_format
form_for(@post, :format => :json, :html => { :id => "edit_post_123", :class => "edit_post" }) do |f|
concat f.label(:title)
@@ -2204,6 +2226,19 @@ class FormHelperTest < ActionView::TestCase
assert_equal "fields", output
end
+ def test_form_for_only_instantiates_builder_once
+ initialization_count = 0
+ builder_class = Class.new(ActionView::Helpers::FormBuilder) do
+ define_method :initialize do |*args|
+ super(*args)
+ initialization_count += 1
+ end
+ end
+
+ form_for(@post, :builder => builder_class) { }
+ assert_equal 1, initialization_count, 'form builder instantiated more than once'
+ end
+
protected
def protect_against_forgery?
false
diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb
index 3009fa5330..72c2609a48 100644
--- a/actionpack/test/template/form_options_helper_test.rb
+++ b/actionpack/test/template/form_options_helper_test.rb
@@ -515,6 +515,14 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_select_with_multiple_and_with_explicit_name_ending_with_brackets
+ output_buffer = select(:post, :category, "", {}, :multiple => true, :name => 'post[category][]')
+ assert_dom_equal(
+ "<input type=\"hidden\" name=\"post[category][]\" value=\"\"/><select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>",
+ output_buffer
+ )
+ end
+
def test_select_with_multiple_and_disabled_to_add_disabled_hidden_input
output_buffer = select(:post, :category, "", {}, :multiple => true, :disabled => true)
assert_dom_equal(
diff --git a/actionpack/test/template/html-scanner/sanitizer_test.rb b/actionpack/test/template/html-scanner/sanitizer_test.rb
index 62ad6be680..dee60c9d00 100644
--- a/actionpack/test/template/html-scanner/sanitizer_test.rb
+++ b/actionpack/test/template/html-scanner/sanitizer_test.rb
@@ -176,6 +176,7 @@ class SanitizerTest < ActionController::TestCase
%(<IMG SRC="jav&#x0A;ascript:alert('XSS');">),
%(<IMG SRC="jav&#x0D;ascript:alert('XSS');">),
%(<IMG SRC=" &#14; javascript:alert('XSS');">),
+ %(<IMG SRC="javascript&#x3a;alert('XSS');">),
%(<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`>)].each_with_index do |img_hack, i|
define_method "test_should_not_fall_for_xss_image_hack_#{i+1}" do
assert_sanitized img_hack, "<img>"
@@ -210,7 +211,7 @@ class SanitizerTest < ActionController::TestCase
# TODO: Clean up
def test_should_sanitize_attributes
- assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="'&gt;&lt;script&gt;alert()&lt;/script&gt;">blah</span>)
+ assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="#{CGI.escapeHTML "'><script>alert()</script>"}">blah</span>)
end
def test_should_sanitize_illegal_style_properties
@@ -256,6 +257,11 @@ class SanitizerTest < ActionController::TestCase
assert_equal '', sanitize_css(raw)
end
+ def test_should_sanitize_across_newlines
+ raw = %(\nwidth:\nexpression(alert('XSS'));\n)
+ assert_equal '', sanitize_css(raw)
+ end
+
def test_should_sanitize_img_vbscript
assert_sanitized %(<img src='vbscript:msgbox("XSS")' />), '<img />'
end
@@ -276,6 +282,15 @@ class SanitizerTest < ActionController::TestCase
assert_sanitized "<span class=\"\\", "<span class=\"\\\">"
end
+ def test_x03a
+ assert_sanitized %(<a href="javascript&#x3a;alert('XSS');">), "<a>"
+ assert_sanitized %(<a href="javascript&#x003a;alert('XSS');">), "<a>"
+ assert_sanitized %(<a href="http&#x3a;//legit">), %(<a href="http://legit">)
+ assert_sanitized %(<a href="javascript&#x3A;alert('XSS');">), "<a>"
+ assert_sanitized %(<a href="javascript&#x003A;alert('XSS');">), "<a>"
+ assert_sanitized %(<a href="http&#x3A;//legit">), %(<a href="http://legit">)
+ end
+
protected
def assert_sanitized(input, expected = nil)
@sanitizer ||= HTML::WhiteListSanitizer.new
diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb
index 8d679aac1d..37ce3cf6b6 100644
--- a/actionpack/test/template/number_helper_test.rb
+++ b/actionpack/test/template/number_helper_test.rb
@@ -251,6 +251,11 @@ class NumberHelperTest < ActionView::TestCase
assert_equal '4.5 tens', number_to_human(45, :units => {:unit => "", :ten => ' tens '})
end
+ def test_number_to_human_with_custom_units_that_are_missing_the_needed_key
+ assert_equal '123', number_to_human(123, :units => {:thousand => 'k'})
+ assert_equal '123', number_to_human(123, :units => {})
+ end
+
def test_number_to_human_with_custom_format
assert_equal '123 times Thousand', number_to_human(123456, :format => "%n times %u")
volume = {:unit => "ml", :thousand => "lt", :million => "m3"}
diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb
index f33471ab28..3cb5997a83 100644
--- a/actionpack/test/template/record_tag_helper_test.rb
+++ b/actionpack/test/template/record_tag_helper_test.rb
@@ -81,6 +81,14 @@ class RecordTagHelperTest < ActionView::TestCase
assert_dom_equal expected, actual
end
+ def test_content_tag_for_collection_without_given_block
+ post_1 = RecordTagPost.new.tap { |post| post.id = 101; post.body = "Hello!"; post.persisted = true }
+ post_2 = RecordTagPost.new.tap { |post| post.id = 102; post.body = "World!"; post.persisted = true }
+ expected = %(<li class="record_tag_post" id="record_tag_post_101"></li>\n<li class="record_tag_post" id="record_tag_post_102"></li>)
+ actual = content_tag_for(:li, [post_1, post_2])
+ assert_dom_equal expected, actual
+ end
+
def test_div_for_collection
post_1 = RecordTagPost.new.tap { |post| post.id = 101; post.body = "Hello!"; post.persisted = true }
post_2 = RecordTagPost.new.tap { |post| post.id = 102; post.body = "World!"; post.persisted = true }
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index bfeb6aee1a..72f494c811 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -54,6 +54,16 @@ module RenderTestCases
assert_equal "Hello world", @view.render(:template => "test/one", :formats => [:html])
end
+ def test_render_partial_implicitly_use_format_of_the_rendered_partial
+ @view.lookup_context.formats = [:html]
+ assert_equal "Third level", @view.render(:template => "test/html_template")
+ end
+
+ def test_render_partial_use_last_prepended_format_for_partials_with_the_same_names
+ @view.lookup_context.formats = [:html]
+ assert_equal "\nHTML Template, but JSON partial", @view.render(:template => "test/change_priority")
+ end
+
def test_render_template_with_a_missing_partial_of_another_format
@view.lookup_context.formats = [:html]
assert_raise ActionView::Template::Error, "Missing partial /missing with {:locale=>[:en], :formats=>[:json], :handlers=>[:erb, :builder]}" do
@@ -336,7 +346,7 @@ module RenderTestCases
ActionView::Template.register_template_handler :foo, CustomHandler
assert_equal 'source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo)
end
-
+
def test_render_knows_about_types_registered_when_extensions_are_checked_earlier_in_initialization
ActionView::Template::Handlers.extensions
ActionView::Template.register_template_handler :foo, CustomHandler
@@ -406,6 +416,11 @@ module RenderTestCases
@view.render(:partial => 'test/partial_with_layout_block_content', :layout => 'test/layout_for_partial', :locals => { :name => 'Foo!'})
end
+ def test_render_partial_with_layout_raises_descriptive_error
+ e = assert_raises(ActionView::MissingTemplate) { @view.render(:partial => 'test/partial', :layout => true) }
+ assert_match "Missing partial /true with", e.message
+ end
+
def test_render_with_nested_layout
assert_equal %(<title>title</title>\n\n<div id="column">column</div>\n<div id="content">content</div>\n),
@view.render(:file => "test/nested_layout", :layout => "layouts/yield")
diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb
index 72d03e43e9..1ad9bcab5a 100644
--- a/actionpack/test/template/sprockets_helper_test.rb
+++ b/actionpack/test/template/sprockets_helper_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'sprockets'
require 'sprockets/helpers/rails_helper'
-require 'mocha'
+require 'mocha/setup'
class SprocketsHelperTest < ActionView::TestCase
include Sprockets::Helpers::RailsHelper
@@ -266,6 +266,24 @@ class SprocketsHelperTest < ActionView::TestCase
javascript_include_tag('/javascripts/application')
assert_match %r{<script src="/assets/xmlhr-[0-9a-f]+.js\?body=1" type="text/javascript"></script>\n<script src="/assets/application-[0-9a-f]+.js\?body=1" type="text/javascript"></script>},
javascript_include_tag(:application)
+ assert_match %r{<script src="/assets/xmlhr-[0-9a-f]+.js\?body=1" type="text/javascript"></script>\n<script src="/assets/application-[0-9a-f]+.js\?body=1" type="text/javascript"></script>\n<script src="/assets/extra-[0-9a-f]+.js\?body=1" type="text/javascript"></script>},
+ javascript_include_tag(:application, :extra)
+ end
+
+ test "precompiled assets with an extension when no JS runtime is available" do
+ @config.assets.compile = false
+ @config.assets.digests = {'foo.min.js' => 'foo.min-f00.js'}
+ Sprockets::Index.any_instance.stubs(:build_asset).raises
+ assert_nothing_raised { javascript_include_tag('foo.min') }
+ end
+
+ test "assets that exist on filesystem don't need to go through Sprockets" do
+ @config.assets.digest = false
+ @config.assets.debug = true
+
+ Rails.application.assets.expects(:resolve).never
+
+ asset_paths.asset_for(FIXTURES.join("sprockets/app/javascripts/foo.min.js"), 'min')
end
test "stylesheet path through asset_path" do
@@ -324,6 +342,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)
+ 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" />\n<link href="/assets/extra-[0-9a-f]+.css\?body=1" media="screen" rel="stylesheet" type="text/css" />},
+ stylesheet_link_tag(:application, :extra)
+
assert_match %r{<link href="/assets/style-[0-9a-f]+.css\?body=1" media="print" rel="stylesheet" type="text/css" />\n<link href="/assets/application-[0-9a-f]+.css\?body=1" media="print" rel="stylesheet" type="text/css" />},
stylesheet_link_tag(:application, :media => "print")
end
diff --git a/actionpack/test/template/sprockets_helper_with_routes_test.rb b/actionpack/test/template/sprockets_helper_with_routes_test.rb
index bcbd81a7dd..bc253ea0fd 100644
--- a/actionpack/test/template/sprockets_helper_with_routes_test.rb
+++ b/actionpack/test/template/sprockets_helper_with_routes_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'sprockets'
require 'sprockets/helpers/rails_helper'
-require 'mocha'
+require 'mocha/setup'
class SprocketsHelperWithRoutesTest < ActionView::TestCase
include Sprockets::Helpers::RailsHelper
@@ -54,4 +54,4 @@ class SprocketsHelperWithRoutesTest < ActionView::TestCase
stylesheet_link_tag(:application)
end
-end \ No newline at end of file
+end
diff --git a/actionpack/test/template/template_test.rb b/actionpack/test/template/template_test.rb
index 5880eb2bd4..b7f785fe3a 100644
--- a/actionpack/test/template/template_test.rb
+++ b/actionpack/test/template/template_test.rb
@@ -1,3 +1,4 @@
+# encoding: US-ASCII
require "abstract_unit"
require "logger"
@@ -25,6 +26,10 @@ class TestERBTemplate < ActiveSupport::TestCase
"Hello"
end
+ def apostrophe
+ "l'apostrophe"
+ end
+
def partial
ActionView::Template.new(
"<%= @virtual_path %>",
@@ -47,7 +52,7 @@ class TestERBTemplate < ActiveSupport::TestCase
end
end
- def new_template(body = "<%= hello %>", details = {})
+ def new_template(body = "<%= hello %>", details = { :format => :html })
ActionView::Template.new(body, "hello template", ERBHandler, {:virtual_path => "hello"}.merge!(details))
end
@@ -64,6 +69,16 @@ class TestERBTemplate < ActiveSupport::TestCase
assert_equal "Hello", render
end
+ def test_basic_template_does_html_escape
+ @template = new_template("<%= apostrophe %>")
+ assert_equal "l&#x27;apostrophe", render
+ end
+
+ def test_text_template_does_not_html_escape
+ @template = new_template("<%= apostrophe %> <%== apostrophe %>", :format => :text)
+ assert_equal "l'apostrophe l'apostrophe", render
+ end
+
def test_template_loses_its_source_after_rendering
@template = new_template
render
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 6f09c1cb43..470e76b7d3 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,4 +1,30 @@
-## Rails 3.2.10 ##
+## unreleased ##
+
+* No changes.
+
+
+## Rails 3.2.13 (Mar 18, 2013) ##
+
+* Specify type of singular association during serialization *Steve Klabnik*
+
+
+## Rails 3.2.12 (Feb 11, 2013) ##
+
+* Fix issue with `attr_protected` where malformed input could circumvent protection.
+ CVE-2013-0276
+
+ *joernchen*
+
+
+## Rails 3.2.11 (Jan 8, 2013) ##
+
+* No changes.
+
+
+## Rails 3.2.10 (Jan 2, 2013) ##
+
+* No changes.
+
## Rails 3.2.9 (Nov 12, 2012) ##
@@ -13,24 +39,29 @@
*Carlos Antonio da Silva*
+
## Rails 3.2.8 (Aug 9, 2012) ##
* No changes.
+
## Rails 3.2.7 (Jul 26, 2012) ##
* `validates_inclusion_of` and `validates_exclusion_of` now accept `:within` option as alias of `:in` as documented.
* Fix the the backport of the object dup with the ruby 1.9.3p194.
+
## Rails 3.2.6 (Jun 12, 2012) ##
* No changes.
+
## Rails 3.2.4 (May 31, 2012) ##
* No changes.
+
## Rails 3.2.3 (March 30, 2012) ##
* No changes.
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 00bd68512b..7eaee050cd 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -84,7 +84,7 @@ module ActiveModel
end
# Backport dup from 1.9 so that #initialize_dup gets called
- unless Object.respond_to?(:initialize_dup)
+ unless Object.respond_to?(:initialize_dup, true)
def dup # :nodoc:
copy = super
copy.initialize_dup(self)
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index 2dc7d00f52..a63856045f 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -144,7 +144,12 @@ module ActiveModel
end
else
merged_options[:root] = association.to_s
- records.to_xml(merged_options)
+
+ unless records.class.to_s.underscore == association.to_s
+ merged_options[:type] = records.class.name
+ end
+
+ records.to_xml merged_options
end
end
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 45d8677fa0..c7e508958b 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -173,7 +173,7 @@ module ActiveModel
end
# Backport dup from 1.9 so that #initialize_dup gets called
- unless Object.respond_to?(:initialize_dup)
+ unless Object.respond_to?(:initialize_dup, true)
def dup # :nodoc:
copy = super
copy.initialize_dup(self)
diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb
index 51a678d151..ec0efc72b7 100644
--- a/activemodel/lib/active_model/version.rb
+++ b/activemodel/lib/active_model/version.rb
@@ -2,7 +2,7 @@ module ActiveModel
module VERSION #:nodoc:
MAJOR = 3
MINOR = 2
- TINY = 11
+ TINY = 13
PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb
index 83db8d21f4..62c2b2035f 100644
--- a/activemodel/test/cases/serializers/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializers/xml_serialization_test.rb
@@ -6,12 +6,12 @@ require 'ostruct'
class Contact
include ActiveModel::Serializers::Xml
- attr_accessor :address, :friends
+ attr_accessor :address, :friends, :contact
remove_method :attributes if method_defined?(:attributes)
def attributes
- instance_values.except("address", "friends")
+ instance_values.except("address", "friends", "contact")
end
end
@@ -55,6 +55,9 @@ class XmlSerializationTest < ActiveModel::TestCase
@contact.address.state = "CA"
@contact.address.zip = 11111
@contact.friends = [Contact.new, Contact.new]
+ @related_contact = SerializableContact.new
+ @related_contact.name = "related"
+ @contact.contact = @related_contact
end
test "should serialize default root" do
@@ -203,4 +206,9 @@ class XmlSerializationTest < ActiveModel::TestCase
assert_match %r{<friends>}, xml
assert_match %r{<friend>}, xml
end
+
+ test "association with sti" do
+ xml = @contact.to_xml(:include => :contact)
+ assert xml.include?(%(<contact type="SerializableContact">))
+ end
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 93c5aba851..5e5e1490c4 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,4 +1,343 @@
-## Rails 3.2.12 (unreleased) ##
+## unreleased ##
+
+* Removed warning when `auto_explain_threshold_in_seconds` is set and the
+ connection adapter doesn't support explain.
+ This is causing a regression since the Active Record Railtie is trying to
+ connect to the development database in the application boot.
+
+ *Rafael Mendonça França*
+
+* Do not reset `inheritance_column` when it's set explicitly.
+ Backport of #5327.
+
+ *kennyj + Fred Wu*
+
+* Fix a problem wrong exception is occured
+ when raising no translatable exception in PostgreSQL.
+
+ *kennyj*
+
+* Resets the postgres search path in the structure.sql after the structure
+ is dumped in order to find schema_migrations table when multiples schemas
+ are used.
+ Fixes #9796.
+
+ *Juan M. Cuello + Dembskiy Alexander*
+
+* Reload the association target if it's stale. `@stale_state` should be nil
+ when a model isn't saved.
+ Fixes #7526.
+
+ *Larry Lv*
+
+* Don't read CSV files during execution of `db:fixtures:load`. CSV support for
+ fixtures was removed some time ago but the task was still loading them, even
+ though later the code was looking for the related yaml file instead.
+
+ *kennyj*
+
+
+## Rails 3.2.13 (Mar 18, 2013) ##
+
+* Chaining multiple preloaded scopes will correctly preload all the scopes
+ at the same time.
+
+ *Chris Geihsler*
+
+* Reverted 921a296a3390192a71abeec6d9a035cc6d1865c8, 'Quote numeric values
+ compared to string columns.' This caused several regressions.
+
+ *Steve Klabnik*
+
+* Fix overriding of attributes by `default_scope` on `ActiveRecord::Base#dup`.
+
+ *Hiroshige UMINO*
+
+* Fix issue with overriding Active Record reader methods with a composed object
+ and using that attribute as the scope of a `uniqueness_of` validation.
+ Backport #7072.
+
+ *Peter Brown*
+
+* Sqlite now preserves custom primary keys when copying or altering tables.
+ Fixes #9367.
+ Backport #2312.
+
+ *Sean Scally + Yves Senn*
+
+* Preloading `has_many :through` associations with conditions won't
+ cache the `:through` association. This will prevent invalid
+ subsets to be cached.
+ Fixes #8423.
+ Backport #9252.
+
+ Example:
+
+ class User
+ has_many :posts
+ has_many :recent_comments, -> { where('created_at > ?', 1.week.ago) }, :through => :posts
+ end
+
+ a_user = User.includes(:recent_comments).first
+
+ # this is preloaded
+ a_user.recent_comments
+
+ # fetching the recent_comments through the posts association won't preload it.
+ a_user.posts
+
+ *Yves Senn*
+
+* Fix handling of dirty time zone aware attributes
+
+ Previously, when `time_zone_aware_attributes` were enabled, after
+ changing a datetime or timestamp attribute and then changing it back
+ to the original value, `changed_attributes` still tracked the
+ attribute as changed. This caused `[attribute]_changed?` and
+ `changed?` methods to return true incorrectly.
+
+ Example:
+
+ in_time_zone 'Paris' do
+ order = Order.new
+ original_time = Time.local(2012, 10, 10)
+ order.shipped_at = original_time
+ order.save
+ order.changed? # => false
+
+ # changing value
+ order.shipped_at = Time.local(2013, 1, 1)
+ order.changed? # => true
+
+ # reverting to original value
+ order.shipped_at = original_time
+ order.changed? # => false, used to return true
+ end
+
+ Backport of #9073
+ Fixes #8898
+
+ *Lilibeth De La Cruz*
+
+* Fix counter cache columns not updated when replacing `has_many :through`
+ associations.
+ Backport #8400.
+ Fix #7630.
+
+ *Matthew Robertson*
+
+* Don't update `column_defaults` when calling destructive methods on column with default value.
+ Backport c517602.
+ Fix #6115.
+
+ *Piotr Sarnacki + Aleksey Magusev + Alan Daud*
+
+* When `#count` is used in conjunction with `#uniq` we perform `count(:distinct => true)`.
+ Fix #6865.
+
+ Example:
+
+ relation.uniq.count # => SELECT COUNT(DISTINCT *)
+
+ *Yves Senn + Kaspar Schiess*
+
+* Fix `ActiveRecord::Relation#pluck` when columns or tables are reserved words.
+ Backport #7536.
+ Fix #8968.
+
+ *Ian Lesperance + Yves Senn + Kaspar Schiess*
+
+* Don't run explain on slow queries for database adapters that don't support it.
+ Backport #6197.
+
+ *Blake Smith*
+
+* Revert round usec when comparing timestamp attributes in the dirty tracking.
+ Fixes #8460.
+
+ *Andrew White*
+
+* Revert creation of through association models when using `collection=[]`
+ on a `has_many :through` association from an unsaved model.
+ Fix #7661, #8269.
+
+ *Ernie Miller*
+
+* Fix undefined method `to_i` when calling `new` on a scope that uses an
+ Array; Fix FloatDomainError when setting integer column to NaN.
+ Fixes #8718, #8734, #8757.
+
+ *Jason Stirk + Tristan Harward*
+
+* Serialized attributes can be serialized in integer columns.
+ Fix #8575.
+
+ *Rafael Mendonça França*
+
+* Keep index names when using `alter_table` with sqlite3.
+ Fix #3489.
+ Backport #8522.
+
+ *Yves Senn*
+
+* Recognize migrations placed in directories containing numbers and 'rb'.
+ Fix #8492.
+ Backport of #8500.
+
+ *Yves Senn*
+
+* Add `ActiveRecord::Base.cache_timestamp_format` class attribute to control
+ the format of the timestamp value in the cache key.
+ This allows users to improve the precision of the cache key.
+ Fixes #8195.
+
+ *Rafael Mendonça França*
+
+* Add `:nsec` date format. This can be used to improve the precision of cache key.
+ Please note that this format only works with Ruby 1.9, Ruby 1.8 will ignore it completely.
+
+ *Jamie Gaskins*
+
+* Unscope `update_column(s)` query to ignore default scope.
+
+ When applying `default_scope` to a class with a where clause, using
+ `update_column(s)` could generate a query that would not properly update
+ the record due to the where clause from the `default_scope` being applied
+ to the update query.
+
+ class User < ActiveRecord::Base
+ default_scope where(active: true)
+ end
+
+ user = User.first
+ user.active = false
+ user.save!
+
+ user.update_column(:active, true) # => false
+
+ In this situation we want to skip the default_scope clause and just
+ update the record based on the primary key. With this change:
+
+ user.update_column(:active, true) # => true
+
+ Backport of #8436 fix.
+
+ *Carlos Antonio da Silva*
+
+* Fix performance problem with primary_key method in PostgreSQL adapter when having many schemas.
+ Uses pg_constraint table instead of pg_depend table which has many records in general.
+ Fix #8414
+
+ *kennyj*
+
+* Do not instantiate intermediate Active Record objects when eager loading.
+ These records caused `after_find` to run more than expected.
+ Fix #3313
+ Backport of #8403
+
+ *Yves Senn*
+
+* Fix `pluck` to work with joins. Backport of #4942.
+
+ *Carlos Antonio da Silva*
+
+* Fix a problem with `translate_exception` method in a non English environment.
+ Backport of #6397.
+
+ *kennyj*
+
+* Fix dirty attribute checks for TimeZoneConversion with nil and blank
+ datetime attributes. Setting a nil datetime to a blank string should not
+ result in a change being flagged.
+ Fixes #8310.
+ Backport of #8311.
+
+ *Alisdair McDiarmid*
+
+* Prevent mass assignment to the type column of polymorphic associations when using `build`.
+ Fixes #8265.
+ Backport of #8291.
+
+ *Yves Senn*
+
+* When running migrations on Postgresql, the `:limit` option for `binary` and `text` columns is
+ silently dropped.
+ Previously, these migrations caused sql exceptions, because Postgresql doesn't support limits
+ on these types.
+
+ *Victor Costan*
+
+* `#pluck` can be used on a relation with `select` clause.
+ Fixes #7551.
+ Backport of #8176.
+
+ Example:
+
+ Topic.select([:approved, :id]).order(:id).pluck(:id)
+
+ *Yves Senn*
+
+* Use `nil?` instead of `blank?` to check whether dynamic finder with a bang
+ should raise RecordNotFound.
+ Fixes #7238.
+
+ *Nikita Afanasenko*
+
+* Fix deleting from a HABTM join table upon destroying an object of a model
+ with optimistic locking enabled.
+ Fixes #5332.
+
+ *Nick Rogers*
+
+* Use query cache/uncache when using ENV["DATABASE_URL"].
+ Fixes #6951.
+ Backport of #8074.
+
+ *kennyj*
+
+* Do not create useless database transaction when building `has_one` association.
+
+ Example:
+
+ User.has_one :profile
+ User.new.build_profile
+
+ Backport of #8154.
+
+ *Bogdan Gusiev*
+
+* `AR::Base#attributes_before_type_cast` now returns unserialized values for serialized attributes.
+
+ *Nikita Afanasenko*
+
+* Fix issue that raises `NameError` when overriding the `accepts_nested_attributes` in child classes.
+
+ Before:
+
+ class Shared::Person < ActiveRecord::Base
+ has_one :address
+
+ accepts_nested_attributes :address, :reject_if => :all_blank
+ end
+
+ class Person < Shared::Person
+ accepts_nested_attributes :address
+ end
+
+ Person
+ #=> NameError: method `address_attributes=' not defined in Person
+
+ After:
+
+ Person
+ #=> Person(id: integer, ...)
+
+ Fixes #8131.
+
+ *Gabriel Sobrinho, Ricardo Henrique*
+
+
+## Rails 3.2.12 (Feb 11, 2013) ##
* Quote numeric values being compared to non-numeric columns. Otherwise,
in some database, the string column values will be coerced to a numeric
@@ -10,17 +349,24 @@
*Dylan Smith*
+
## Rails 3.2.11 (Jan 8, 2013) ##
* Fix querying with an empty hash *Damien Mathieu* [CVE-2013-0155]
-## Rails 3.2.10 ##
+
+## Rails 3.2.10 (Jan 2, 2013) ##
* CVE-2012-5664 options hashes should only be extracted if there are extra
parameters
+
## Rails 3.2.9 (Nov 12, 2012) ##
+* Fix `find_in_batches` crashing when IDs are strings and start option is not specified.
+
+ *Alexis Bernard*
+
* Fix issue with collection associations calling first(n)/last(n) and attempting
to set the inverse association when `:inverse_of` was used. Fixes #8087.
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 59c1bad559..99f307922e 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -46,6 +46,7 @@ module ActiveRecord
@loaded = false
IdentityMap.remove(target) if IdentityMap.enabled? && target
@target = nil
+ @stale_state = nil
end
# Reloads the \target and returns +self+ on success.
@@ -128,16 +129,21 @@ module ActiveRecord
# This method is abstract in the sense that it relies on +find_target+,
# which is expected to be provided by descendants.
#
- # If the \target is already \loaded it is just returned. Thus, you can call
- # +load_target+ unconditionally to get the \target.
+ # If the \target is stale(the target no longer points to the record(s) that the
+ # relevant foreign_key(s) refers to.), force reload the \target.
+ #
+ # Otherwise if the \target is already \loaded it is just returned. Thus, you can
+ # call +load_target+ unconditionally to get the \target.
#
# ActiveRecord::RecordNotFound is rescued within the method, and it is
# not reraised. The proxy is \reset and +nil+ is the return value.
def load_target
- if find_target?
+ if (@stale_state && stale_target?) || find_target?
begin
if IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class)
@target = IdentityMap.get(association_class, owner[reflection.foreign_key])
+ elsif @stale_state && stale_target?
+ @target = find_target
end
rescue NameError
nil
@@ -231,7 +237,8 @@ module ActiveRecord
def build_record(attributes, options)
reflection.build_association(attributes, options) do |record|
- attributes = create_scope.except(*(record.changed - [reflection.foreign_key]))
+ skip_assign = [reflection.foreign_key, reflection.type].compact
+ attributes = create_scope.except(*(record.changed - skip_assign))
record.assign_attributes(attributes, :without_protection => true)
end
end
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index 97f531d064..52c67df646 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -72,7 +72,7 @@ module ActiveRecord
end
def stale_state
- owner[reflection.foreign_key].to_s
+ owner[reflection.foreign_key] && owner[reflection.foreign_key].to_s
end
end
end
diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
index 2ee5dbbd70..88ce03a3cd 100644
--- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
@@ -27,7 +27,8 @@ module ActiveRecord
end
def stale_state
- [super, owner[reflection.foreign_type].to_s]
+ foreign_key = super
+ foreign_key && [foreign_key.to_s, owner[reflection.foreign_type].to_s]
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index ce81333aa9..2b9e7ea55d 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -38,20 +38,6 @@ module ActiveRecord
super
end
- def concat_records(records)
- ensure_not_nested
-
- records = super
-
- if owner.new_record? && records
- records.flatten.each do |record|
- build_through_record(record)
- end
- end
-
- records
- end
-
def insert_record(record, validate = true, raise = false)
ensure_not_nested
@@ -153,6 +139,11 @@ module ActiveRecord
delete_through_records(records)
+ if source_reflection.options[:counter_cache]
+ counter = source_reflection.counter_cache_column
+ klass.decrement_counter counter, records.map(&:id)
+ end
+
if through_reflection.macro == :has_many && update_through_counter?(method)
update_counter(-count, through_reflection)
end
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index 501ebe7c5b..56f9013d61 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -11,7 +11,7 @@ module ActiveRecord
# If target and record are nil, or target is equal to record,
# we don't need to have transaction.
if (target || record) && target != record
- reflection.klass.transaction do
+ transaction_if(save) do
remove_target!(options[:dependent]) if target && !target.destroyed?
if record
@@ -70,6 +70,14 @@ module ActiveRecord
def nullify_owner_attributes(record)
record[reflection.foreign_key] = nil
end
+
+ def transaction_if(value)
+ if value
+ reflection.klass.transaction { yield }
+ else
+ yield
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index ad6374d09a..e052b00403 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -37,7 +37,8 @@ module ActiveRecord
through_records = Array.wrap(owner.send(through_reflection.name))
# Dont cache the association - we would only be caching a subset
- if reflection.options[:source_type] && through_reflection.collection?
+ if (preload_options != through_options) ||
+ (reflection.options[:source_type] && through_reflection.collection?)
owner.association(through_reflection.name).reset
end
@@ -55,8 +56,7 @@ module ActiveRecord
through_options[:include] = options[:include] || options[:source]
through_options[:conditions] = options[:conditions]
end
-
- through_options[:order] = options[:order]
+ through_options[:order] = options[:order] if options.has_key?(:order)
end
through_options
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index fd0e90aaf0..be890e5767 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -62,7 +62,7 @@ module ActiveRecord
# properly support stale-checking for nested associations.
def stale_state
if through_reflection.macro == :belongs_to
- owner[through_reflection.foreign_key].to_s
+ owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 00023b0b6c..deb89f3ac1 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -90,6 +90,14 @@ module ActiveRecord
end
end
+ def _field_changed?(attr, old, value)
+ if self.class.serialized_attributes.include?(attr)
+ old != value
+ else
+ super
+ end
+ end
+
def read_attribute_before_type_cast(attr_name)
if serialized_attributes.include?(attr_name)
super.unserialized_value
@@ -97,6 +105,16 @@ module ActiveRecord
super
end
end
+
+ def attributes_before_type_cast
+ super.dup.tap do |attributes|
+ self.class.serialized_attributes.each_key do |key|
+ if attributes.key?(key)
+ attributes[key] = attributes[key].unserialized_value
+ end
+ end
+ end
+ end
end
end
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 4c3d5eed4c..6f3112e654 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -37,18 +37,16 @@ module ActiveRecord
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
method_body, line = <<-EOV, __LINE__ + 1
def #{attr_name}=(original_time)
+ original_time = nil if original_time.blank?
time = original_time
unless time.acts_like?(:time)
time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
end
- zoned_time = time && time.in_time_zone rescue nil
- rounded_time = round_usec(zoned_time)
- rounded_value = round_usec(read_attribute("#{attr_name}"))
- if (rounded_value != rounded_time) || (!rounded_value && original_time)
- write_attribute("#{attr_name}", original_time)
- #{attr_name}_will_change!
- @attributes_cache["#{attr_name}"] = zoned_time
- end
+ time = time.in_time_zone rescue nil if time
+ previous_time = attribute_changed?("#{attr_name}") ? changed_attributes["#{attr_name}"] : read_attribute(:#{attr_name})
+ write_attribute(:#{attr_name}, original_time)
+ #{attr_name}_will_change! if previous_time != time
+ @attributes_cache["#{attr_name}"] = time
end
EOV
generated_attribute_methods.module_eval(method_body, __FILE__, line)
@@ -62,12 +60,6 @@ module ActiveRecord
time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && column.type.in?([:datetime, :timestamp])
end
end
-
- private
- def round_usec(value)
- return unless value
- value.change(:usec => 0)
- end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index 8c6fa90a28..16aed7348f 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -54,12 +54,13 @@ module ActiveRecord
end
def convert_number_column_value(value)
- if value == false
+ case value
+ when FalseClass
0
- elsif value == true
+ when TrueClass
1
- elsif value.is_a?(String) && value.blank?
- nil
+ when String
+ value.presence
else
value
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 62c8110274..5bd1721439 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -479,7 +479,8 @@ module ActiveRecord #:nodoc:
# # Instantiates a single new object bypassing mass-assignment security
# User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
def initialize(attributes = nil, options = {})
- @attributes = self.class.initialize_attributes(self.class.column_defaults.dup)
+ defaults = Hash[self.class.column_defaults.map { |k, v| [k, v.duplicable? ? v.dup : v] }]
+ @attributes = self.class.initialize_attributes(defaults)
@association_cache = {}
@aggregation_cache = {}
@attributes_cache = {}
@@ -552,12 +553,11 @@ module ActiveRecord #:nodoc:
@new_record = true
ensure_proper_type
- populate_with_current_scope_attributes
super
end
# Backport dup from 1.9 so that initialize_dup() gets called
- unless Object.respond_to?(:initialize_dup)
+ unless Object.respond_to?(:initialize_dup, true)
def dup # :nodoc:
copy = super
copy.initialize_dup(self)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index db99c3fbef..2703c2ed70 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -266,7 +266,7 @@ module ActiveRecord
# Inserts the given fixture into the table. Overridden in adapters that require
# something beyond a simple insert (eg. Oracle).
def insert_fixture(fixture, table_name)
- columns = Hash[columns(table_name).map { |c| [c.name, c] }]
+ columns = schema_cache.columns_hash(table_name)
key_list = []
value_list = fixture.map do |name, value|
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index fe0b1959f6..f93c7cd74a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -25,19 +25,13 @@ module ActiveRecord
when true, false
if column && column.type == :integer
value ? '1' : '0'
- elsif column && [:text, :string, :binary].include?(column.type)
- value ? "'1'" : "'0'"
else
value ? quoted_true : quoted_false
end
# BigDecimals need to be put in a non-normalized form and quoted.
when nil then "NULL"
- when Numeric, ActiveSupport::Duration
- value = BigDecimal === value ? value.to_s('F') : value.to_s
- if column && ![:integer, :float, :decimal].include?(column.type)
- value = "'#{value}'"
- end
- value
+ when BigDecimal then value.to_s('F')
+ when Numeric then value.to_s
when Date, Time then "'#{quoted_date(value)}'"
when Symbol then "'#{quote_string(value.to_s)}'"
else
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index ddb6896257..6a5cff6acd 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -42,8 +42,8 @@ module ActiveRecord
# Represents the schema of an SQL table in an abstract way. This class
# provides methods for manipulating the schema representation.
#
- # Inside migration files, the +t+ object in +create_table+ and
- # +change_table+ is actually of this type:
+ # Inside migration files, the +t+ object in +create_table+
+ # is actually of this type:
#
# class SomeMigration < ActiveRecord::Migration
# def up
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 61c5e8040e..abccc3af24 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -199,6 +199,8 @@ module ActiveRecord
if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
s = column.class.string_to_binary(value).unpack("H*")[0]
"x'#{s}'"
+ elsif value.kind_of?(BigDecimal)
+ value.to_s("F")
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index e6269c78cf..e22d9f7222 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -175,7 +175,7 @@ module ActiveRecord
when TrueClass, FalseClass
value ? 1 : 0
else
- value.to_i
+ value.to_i rescue nil
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index c132692249..4850b68051 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -2,7 +2,7 @@ require 'active_record/connection_adapters/abstract_mysql_adapter'
require 'active_record/connection_adapters/statement_pool'
require 'active_support/core_ext/hash/keys'
-gem 'mysql', '~> 2.8.1'
+gem 'mysql', '~> 2.8'
require 'mysql'
class Mysql
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 80a0c6d13c..cbbb195458 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -315,8 +315,6 @@ module ActiveRecord
@visitor = BindSubstitution.new self
end
- connection_parameters.delete :prepared_statements
-
@connection_parameters, @config = connection_parameters, config
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
@@ -988,12 +986,11 @@ module ActiveRecord
# Returns just a table's primary key
def primary_key(table)
row = exec_query(<<-end_sql, 'SCHEMA').rows.first
- SELECT DISTINCT(attr.attname)
+ SELECT attr.attname
FROM pg_attribute attr
- INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
WHERE cons.contype = 'p'
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
+ AND cons.conrelid = '#{quote_table_name(table)}'::regclass
end_sql
row && row.first
@@ -1078,6 +1075,13 @@ module ActiveRecord
when nil, 0..0x3fffffff; super(type)
else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
end
+ when 'text'
+ # PostgreSQL doesn't support limits on text columns.
+ # The hard limit is 1Gb, according to section 8.3 in the manual.
+ case limit
+ when nil, 0..0x3fffffff; super(type)
+ else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
+ end
when 'integer'
return 'integer' unless limit
@@ -1135,11 +1139,17 @@ module ActiveRecord
@connection.server_version
end
+ # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
+ FOREIGN_KEY_VIOLATION = "23503"
+ UNIQUE_VIOLATION = "23505"
+
def translate_exception(exception, message)
- case exception.message
- when /duplicate key value violates unique constraint/
+ return exception unless exception.respond_to?(:result)
+
+ case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
+ when UNIQUE_VIOLATION
RecordNotUnique.new(message, exception)
- when /violates foreign key constraint/
+ when FOREIGN_KEY_VIOLATION
InvalidForeignKey.new(message, exception)
else
super
diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
index 4e8932a695..bc8d24a03f 100644
--- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module ConnectionAdapters
class SchemaCache
- attr_reader :columns, :columns_hash, :primary_keys, :tables
+ attr_reader :primary_keys, :tables
attr_reader :connection
def initialize(conn)
@@ -30,6 +30,25 @@ module ActiveRecord
@tables[name] = connection.table_exists?(name)
end
+ # Get the columns for a table
+ def columns(table = nil)
+ if table
+ @columns[table]
+ else
+ @columns
+ end
+ end
+
+ # Get the columns for a table as a hash, key is the column name
+ # value is the column object.
+ def columns_hash(table = nil)
+ if table
+ @columns_hash[table]
+ else
+ @columns_hash
+ end
+ end
+
# Clears out internal caches
def clear!
@columns.clear
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index e80b465bab..455560aa55 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -490,7 +490,11 @@ module ActiveRecord
end
def copy_table(from, to, options = {}) #:nodoc:
- options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))
+ from_primary_key = primary_key(from)
+ options[:primary_key] = from_primary_key if from_primary_key != 'id'
+ unless options[:primary_key]
+ options[:id] = !columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == from_primary_key
+ end
create_table(to, options) do |definition|
@definition = definition
columns(from).each do |column|
@@ -504,7 +508,7 @@ module ActiveRecord
:precision => column.precision, :scale => column.scale,
:null => column.null)
end
- @definition.primary_key(primary_key(from)) if primary_key(from)
+ @definition.primary_key(from_primary_key) if from_primary_key
yield @definition if block_given?
end
@@ -530,7 +534,7 @@ module ActiveRecord
unless columns.empty?
# index name can't be the same
- opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
+ opts = { :name => name.gsub(/(^|_)(#{from})_/, "\\1#{to}_") }
opts[:unique] = true if index.unique
add_index(to, columns, opts)
end
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb
index bdccf535ea..236834fd84 100644
--- a/activerecord/lib/active_record/explain.rb
+++ b/activerecord/lib/active_record/explain.rb
@@ -11,11 +11,12 @@ module ActiveRecord
end
end
- # If auto explain is enabled, this method triggers EXPLAIN logging for the
- # queries triggered by the block if it takes more than the threshold as a
- # whole. That is, the threshold is not checked against each individual
- # query, but against the duration of the entire block. This approach is
- # convenient for relations.
+ # If the database adapter supports explain and auto explain is enabled,
+ # this method triggers EXPLAIN logging for the queries triggered by the
+ # block if it takes more than the threshold as a whole. That is, the
+ # threshold is not checked against each individual query, but against the
+ # duration of the entire block. This approach is convenient for relations.
+
#
# The available_queries_for_explain thread variable collects the queries
# to be explained. If the value is nil, it means queries are not being
@@ -26,7 +27,7 @@ module ActiveRecord
threshold = auto_explain_threshold_in_seconds
current = Thread.current
- if threshold && current[:available_queries_for_explain].nil?
+ if connection.supports_explain? && threshold && current[:available_queries_for_explain].nil?
begin
queries = current[:available_queries_for_explain] = []
start = Time.now
diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb
index 2c42f4cca5..f2ace18d44 100644
--- a/activerecord/lib/active_record/integration.rb
+++ b/activerecord/lib/active_record/integration.rb
@@ -1,5 +1,16 @@
module ActiveRecord
module Integration
+ extend ActiveSupport::Concern
+
+ included do
+ ##
+ # :singleton-method:
+ # Indicates the format used to generate the timestamp format in the cache key.
+ # This is +:number+, by default.
+ class_attribute :cache_timestamp_format, :instance_writer => false
+ self.cache_timestamp_format = :number
+ end
+
# Returns a String, which Action Pack uses for constructing an URL to this
# object. The default implementation returns this record's id as a String,
# or nil if this record's unsaved.
@@ -39,7 +50,7 @@ module ActiveRecord
when new_record?
"#{self.class.model_name.cache_key}/new"
when timestamp = self[:updated_at]
- timestamp = timestamp.utc.to_s(:number)
+ timestamp = timestamp.utc.to_s(cache_timestamp_format)
"#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
else
"#{self.class.model_name.cache_key}/#{id}"
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 7deac2588a..b2881991d5 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -102,6 +102,8 @@ module ActiveRecord
def destroy #:nodoc:
return super unless locking_enabled?
+ destroy_associations
+
if persisted?
table = self.class.arel_table
lock_col = self.class.locking_column
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index a25f2c7bca..2b6488f183 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -32,7 +32,11 @@ module ActiveRecord
unless (payload[:binds] || []).empty?
binds = " " + payload[:binds].map { |col,v|
- [col.name, v]
+ if col
+ [col.name, v]
+ else
+ [nil, v]
+ end
}.inspect
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index e6686597b7..e8ec93470d 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -458,7 +458,7 @@ module ActiveRecord
say_with_time "#{method}(#{arg_list})" do
unless reverting?
unless arguments.empty? || method == :execute
- arguments[0] = Migrator.proper_table_name(arguments.first)
+ arguments[0] = Migrator.proper_table_name(arguments.first) unless method == :assume_migrated_upto_version
arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
end
end
@@ -627,7 +627,7 @@ module ActiveRecord
seen = Hash.new false
migrations = files.map do |file|
- version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first
+ version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
raise IllegalMigrationNameError.new(file) unless version
version = version.to_i
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 1517e5ec17..e10e6b4aa8 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -173,6 +173,7 @@ module ActiveRecord
def inheritance_column=(value)
@original_inheritance_column = inheritance_column
@inheritance_column = value.to_s
+ @explicit_inheritance_column = true
end
def set_inheritance_column(value = nil, &block) #:nodoc:
@@ -300,7 +301,8 @@ module ActiveRecord
connection.schema_cache.clear_table_cache!(table_name) if table_exists?
@column_names = @content_columns = @column_defaults = @columns = @columns_hash = nil
- @dynamic_methods_hash = @inheritance_column = nil
+ @dynamic_methods_hash = nil
+ @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
@arel_engine = @relation = nil
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index d2065d701f..05091654c0 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -277,13 +277,14 @@ module ActiveRecord
type = (reflection.collection? ? :collection : :one_to_one)
+ # remove_possible_method :pirate_attributes=
+ #
# def pirate_attributes=(attributes)
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options)
# end
class_eval <<-eoruby, __FILE__, __LINE__ + 1
- if method_defined?(:#{association_name}_attributes=)
- remove_method(:#{association_name}_attributes=)
- end
+ remove_possible_method(:#{association_name}_attributes=)
+
def #{association_name}_attributes=(attributes)
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, mass_assignment_options)
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index fd3380a53c..36b6b5fce2 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -194,7 +194,7 @@ module ActiveRecord
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
raise ActiveRecordError, "can not update on a new record object" unless persisted?
- updated_count = self.class.update_all({ name => value }, self.class.primary_key => id)
+ updated_count = self.class.unscoped.update_all({ name => value }, self.class.primary_key => id)
raw_write_attribute(name, value)
diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb
index 466d148901..2156889a0f 100644
--- a/activerecord/lib/active_record/query_cache.rb
+++ b/activerecord/lib/active_record/query_cache.rb
@@ -6,19 +6,19 @@ module ActiveRecord
module ClassMethods
# Enable the query cache within the block if Active Record is configured.
def cache(&block)
- if ActiveRecord::Base.configurations.blank?
- yield
- else
+ if ActiveRecord::Base.connected?
connection.cache(&block)
+ else
+ yield
end
end
# Disable the query cache within the block if Active Record is configured.
def uncached(&block)
- if ActiveRecord::Base.configurations.blank?
- yield
- else
+ if ActiveRecord::Base.connected?
connection.uncached(&block)
+ else
+ yield
end
end
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index ae6d9c8653..b8aed49854 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -64,10 +64,21 @@ db_namespace = namespace :db do
end
end
+ # If neither encoding nor collation is specified, use the utf-8 defaults.
def mysql_creation_options(config)
- @charset = ENV['CHARSET'] || 'utf8'
- @collation = ENV['COLLATION'] || 'utf8_unicode_ci'
- {:charset => (config['encoding'] || @charset), :collation => (config['collation'] || @collation)}
+ default_charset = ENV['CHARSET'] || 'utf8'
+ default_collation = ENV['COLLATION'] || 'utf8_unicode_ci'
+
+ Hash.new.tap do |options|
+ options[:charset] = config['encoding'] if config.include? 'encoding'
+ options[:collation] = config['collation'] if config.include? 'collation'
+
+ # Set default charset only when collation isn't set.
+ options[:charset] ||= default_charset unless options[:collation]
+
+ # Set default collation only when charset is also default.
+ options[:collation] ||= default_collation if options[:charset] == default_charset
+ end
end
def create_database(config)
@@ -101,9 +112,12 @@ db_namespace = namespace :db do
error_class = config['adapter'] =~ /mysql2/ ? Mysql2::Error : Mysql::Error
end
access_denied_error = 1045
+
+ create_options = mysql_creation_options(config)
+
begin
ActiveRecord::Base.establish_connection(config.merge('database' => nil))
- ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config))
+ ActiveRecord::Base.connection.create_database(config['database'], create_options)
ActiveRecord::Base.establish_connection(config)
rescue error_class => sqlerr
if sqlerr.errno == access_denied_error
@@ -119,7 +133,7 @@ db_namespace = namespace :db do
ActiveRecord::Base.establish_connection(config)
else
$stderr.puts sqlerr.error
- $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['encoding'] || @charset}, collation: #{config['collation'] || @collation}"
+ $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{create_options[:charset]}, collation: #{create_options[:collation]}"
$stderr.puts "(if you set the charset manually, make sure you have a matching collation)" if config['encoding']
end
end
@@ -341,7 +355,7 @@ db_namespace = namespace :db do
base_dir = File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten
fixtures_dir = File.join [base_dir, ENV['FIXTURES_DIR']].compact
- (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.{yml,csv}"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file|
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file|
ActiveRecord::Fixtures.create_fixtures(fixtures_dir, fixture_file)
end
end
@@ -413,17 +427,18 @@ db_namespace = namespace :db do
end
`pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(config['database'])}`
raise 'Error dumping database' if $?.exitstatus == 1
+ File.open(filename, "a") { |f| f << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" }
when /sqlite/
dbfile = config['database']
`sqlite3 #{dbfile} .schema > #{filename}`
when 'sqlserver'
`smoscript -s #{config['host']} -d #{config['database']} -u #{config['username']} -p #{config['password']} -f #{filename} -A -U`
when "firebird"
- set_firebird_env(abcs[Rails.env])
- db_string = firebird_db_string(abcs[Rails.env])
+ set_firebird_env(config)
+ db_string = firebird_db_string(config)
sh "isql -a #{db_string} > #{filename}"
else
- raise "Task not supported by '#{abcs[Rails.env]["adapter"]}'"
+ raise "Task not supported by '#{config['adapter']}'"
end
if ActiveRecord::Base.connection.supports_migrations?
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 4b3b30d6ed..ae1a57545e 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -464,7 +464,7 @@ module ActiveRecord
node.left.relation.name == table_name
}
- Hash[equalities.map { |where| [where.left.name, where.right] }]
+ Hash[equalities.map { |where| [where.left.name, where.right] }].with_indifferent_access
end
def scope_for_create
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 2fd89882ff..14701f668c 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -59,11 +59,11 @@ module ActiveRecord
relation = apply_finder_options(finder_options)
end
- start = options.delete(:start).to_i
+ start = options.delete(:start)
batch_size = options.delete(:batch_size) || 1000
relation = relation.reorder(batch_order).limit(batch_size)
- records = relation.where(table[primary_key].gteq(start)).all
+ records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
while records.any?
records_size = records.size
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 802059db21..1f9dbdc3d4 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -177,8 +177,15 @@ module ActiveRecord
# Person.where(:confirmed => true).limit(5).pluck(:id)
#
def pluck(column_name)
- column_name = column_name.to_s
- klass.connection.select_all(select(column_name).arel).map! do |attributes|
+ if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
+ column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
+ else
+ column_name = column_name.to_s
+ end
+
+ relation = clone
+ relation.select_values = [column_name]
+ klass.connection.select_all(relation.arel).map! do |attributes|
klass.type_cast_attribute(attributes.keys.first, klass.initialize_attributes(attributes))
end
end
@@ -188,7 +195,8 @@ module ActiveRecord
def perform_calculation(operation, column_name, options = {})
operation = operation.to_s.downcase
- distinct = options[:distinct]
+ # If #count is used in conjuction with #uniq it is considered distinct. (eg. relation.uniq.count)
+ distinct = options[:distinct] || self.uniq_value
if operation == "count"
column_name ||= (select_for_count || :all)
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index abc67d9c15..08cfe4f70d 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -253,9 +253,11 @@ module ActiveRecord
orders = relation.order_values.map { |val| val.presence }.compact
values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders)
- relation = relation.dup
+ relation = relation.dup.select(values)
+
+ id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
+ ids_array = id_rows.map {|row| row[primary_key]}
- ids_array = relation.select(values).collect {|row| row[primary_key]}
ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
end
@@ -263,7 +265,7 @@ module ActiveRecord
conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
result = where(conditions).send(match.finder)
- if match.bang? && result.blank?
+ if match.bang? && result.nil?
raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
else
yield(result) if block_given?
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 236fd5cdfd..413b81cc69 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -20,7 +20,7 @@ module ActiveRecord
table = Arel::Table.new(table_name, engine)
end
- attribute = table[column.to_sym]
+ attribute = table[column]
case value
when ActiveRecord::Relation
@@ -51,10 +51,6 @@ module ActiveRecord
when Class
# FIXME: I think we need to deprecate this behavior
attribute.eq(value.name)
- when Integer, ActiveSupport::Duration
- # Arel treats integers as literals, but they should be quoted when compared with strings
- column = engine.connection.schema_cache.columns_hash[table.name][attribute.name.to_s]
- attribute.eq(Arel::Nodes::SqlLiteral.new(engine.connection.quote(value, column)))
else
attribute.eq(value)
end
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index c25570d758..b8e384c2f3 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -17,14 +17,14 @@ module ActiveRecord
if method == :includes
merged_relation = merged_relation.includes(value)
else
- merged_relation.send(:"#{method}_values=", value)
+ merge_relation_method(merged_relation, method, value)
end
end
end
(Relation::MULTI_VALUE_METHODS - [:joins, :where, :order]).each do |method|
value = r.send(:"#{method}_values")
- merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present?
+ merge_relation_method(merged_relation, method, value) if value.present?
end
merged_relation.joins_values += r.joins_values
@@ -144,5 +144,10 @@ module ActiveRecord
relation
end
+ private
+
+ def merge_relation_method(relation, method, value)
+ relation.send(:"#{method}_values=", relation.send(:"#{method}_values") + value)
+ end
end
end
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index 9ceab2eabc..b8d2cd2866 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -26,9 +26,15 @@ module ActiveRecord
private
def hash_rows
- @hash_rows ||= @rows.map { |row|
- Hash[@columns.zip(row)]
- }
+ @hash_rows ||=
+ begin
+ # We freeze the strings to prevent them getting duped when
+ # used as keys in ActiveRecord::Model's @attributes hash
+ columns = @columns.map { |c| c.dup.freeze }
+ @rows.map { |row|
+ Hash[columns.zip(row)]
+ }
+ end
end
end
end
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index d6b0265fb3..767b30f51f 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -161,16 +161,14 @@ module ActiveRecord
# end
#
# def self.titles
- # map(&:title)
+ # pluck(:title)
# end
- #
# end
#
# We are able to call the methods like this:
#
# Article.published.featured.latest_article
# Article.featured.titles
-
def scope(name, scope_options = {})
name = name.to_sym
valid_scope_name?(name)
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 154aacef9f..795088530e 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -26,7 +26,7 @@ module ActiveRecord
relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
Array.wrap(options[:scope]).each do |scope_item|
- scope_value = record.send(scope_item)
+ scope_value = record.read_attribute(scope_item)
relation = relation.and(table[scope_item].eq(scope_value))
end
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index ff9fa279f4..9069d9964f 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -2,7 +2,7 @@ module ActiveRecord
module VERSION #:nodoc:
MAJOR = 3
MINOR = 2
- TINY = 11
+ TINY = 13
PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 0de3786eb8..6c345cd8cc 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -188,6 +188,12 @@ module ActiveRecord
assert_equal "DISTINCT posts.title, posts.updater_id AS alias_0", @connection.distinct("posts.title", ["posts.updater_id desc nulls last"])
end
+ def test_raise_error_when_cannot_translate_exception
+ assert_raise TypeError do
+ @connection.send(:log, nil) { @connection.execute(nil) }
+ end
+ end
+
private
def insert(ctx, data)
binds = data.map { |name, value|
diff --git a/activerecord/test/cases/adapters/postgresql/sql_types_test.rb b/activerecord/test/cases/adapters/postgresql/sql_types_test.rb
new file mode 100644
index 0000000000..d7d40f6385
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/sql_types_test.rb
@@ -0,0 +1,18 @@
+require "cases/helper"
+
+class SqlTypesTest < ActiveRecord::TestCase
+ def test_binary_types
+ assert_equal 'bytea', type_to_sql(:binary, 100_000)
+ assert_raise ActiveRecord::ActiveRecordError do
+ type_to_sql :binary, 4294967295
+ end
+ assert_equal 'text', type_to_sql(:text, 100_000)
+ assert_raise ActiveRecord::ActiveRecordError do
+ type_to_sql :text, 4294967295
+ end
+ end
+
+ def type_to_sql(*args)
+ ActiveRecord::Base.connection.type_to_sql(*args)
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
index 575b4806c1..74288a98d1 100644
--- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
@@ -57,6 +57,14 @@ class CopyTableTest < ActiveRecord::TestCase
end
end
+ def test_copy_table_with_unconventional_primary_key
+ test_copy_table('owners', 'owners_unconventional') do |from, to, options|
+ original_pk = @connection.primary_key('owners')
+ copied_pk = @connection.primary_key('owners_unconventional')
+ assert_equal original_pk, copied_pk
+ end
+ end
+
protected
def copy_table(from, to, options = {})
@connection.copy_table(from, to, {:temporary => true}.merge(options))
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index ec69a36174..614be8760a 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -157,7 +157,6 @@ module ActiveRecord
binary = DualEncoding.new :name => 'いただきます!', :data => str
binary.save!
assert_equal str, binary.data
-
ensure
if "<3".respond_to?(:encode)
DualEncoding.connection.drop_table('dual_encodings')
@@ -167,8 +166,9 @@ module ActiveRecord
def test_type_cast_should_not_mutate_encoding
return skip('only test encoding on 1.9') unless "<3".encoding_aware?
- name = 'hello'.force_encoding(Encoding::ASCII_8BIT)
- owner = Owner.create(:name => name)
+ name = 'hello'.force_encoding(Encoding::ASCII_8BIT)
+ Owner.create(:name => name)
+
assert_equal Encoding::ASCII_8BIT, name.encoding
end
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index 588adc38e3..6556219205 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -11,23 +11,31 @@ if ActiveRecord::Base.connection.supports_migrations?
def teardown
@connection.drop_table :fruits rescue nil
+ @connection.drop_table :"_p_fruits_s_" rescue nil
+ @connection.drop_table :"_p_schema_migrations_s_" rescue nil
end
def test_schema_define
- ActiveRecord::Schema.define(:version => 7) do
- create_table :fruits do |t|
- t.column :color, :string
- t.column :fruit_size, :string # NOTE: "size" is reserved in Oracle
- t.column :texture, :string
- t.column :flavor, :string
- end
- end
+ perform_schema_define!
assert_nothing_raised { @connection.select_all "SELECT * FROM fruits" }
assert_nothing_raised { @connection.select_all "SELECT * FROM schema_migrations" }
assert_equal 7, ActiveRecord::Migrator::current_version
end
+ def test_schema_define_with_table_prefix_and_suffix
+ # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters
+ ActiveRecord::Base.table_name_prefix = '_p_'
+ ActiveRecord::Base.table_name_suffix = '_s_'
+
+ perform_schema_define!
+
+ assert_equal 7, ActiveRecord::Migrator::current_version
+ ensure
+ ActiveRecord::Base.table_name_prefix = ''
+ ActiveRecord::Base.table_name_suffix = ''
+ end
+
def test_schema_raises_an_error_for_invalid_column_type
assert_raise NoMethodError do
ActiveRecord::Schema.define(:version => 8) do
@@ -39,4 +47,17 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
+ private
+
+ def perform_schema_define!
+ ActiveRecord::Schema.define(:version => 7) do
+ create_table :fruits do |t|
+ t.column :color, :string
+ t.column :fruit_size, :string # NOTE: "size" is reserved in Oracle
+ t.column :texture, :string
+ t.column :flavor, :string
+ end
+ end
+ end
+
end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index e7d1893ff3..c9b26895ae 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -14,6 +14,8 @@ require 'models/sponsor'
require 'models/member'
require 'models/essay'
require 'models/toy'
+require 'models/person'
+require 'models/reader'
class BelongsToAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :developers, :projects, :topics,
@@ -66,7 +68,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_id_assignment
apple = Firm.create("name" => "Apple")
citibank = Account.create("credit_limit" => 10)
- assert_raise(NoMethodError) { citibank.firm_id = apple }
+ citibank.firm_id = apple
+ assert_nil citibank.firm_id
end
def test_natural_assignment_with_primary_key
@@ -544,6 +547,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal new_firm.name, "Apple"
end
+ def test_attributes_are_set_without_error_when_initialized_from_belongs_to_association_with_array_in_where_clause
+ new_account = Account.where(:credit_limit => [ 50, 60 ]).new
+ assert_nil new_account.credit_limit
+ end
+
def test_reassigning_the_parent_id_updates_the_object
client = companies(:second_client)
@@ -710,4 +718,16 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal toy, sponsor.reload.sponsorable
end
+
+ def test_saving_nested_association
+ post1, post2 = Post.limit(2)
+ person = Person.new(:first_name => 'foo')
+ reader = Reader.new(:post => post1)
+
+ reader.post_id = post2.id
+ person.readers = [reader]
+
+ assert person.save
+ assert_equal reader.post_id, post2.id
+ end
end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index bf01b46852..42061d3d73 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -592,7 +592,6 @@ class EagerAssociationTest < ActiveRecord::TestCase
# gets raw row hashes from the database and then instantiates them, this test ensures that
# it only instantiates one actual object per record from the database.
def test_has_and_belongs_to_many_should_not_instantiate_same_records_multiple_times
- welcome = posts(:welcome)
categories = Category.includes(:posts)
general = categories.find { |c| c == categories(:general) }
@@ -916,6 +915,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal 3, Developer.find(:all, :include => 'projects', :conditions => 'developers_projects.access_level = 1', :limit => 5).size
end
+ def test_dont_create_temporary_active_record_instances
+ Developer.instance_count = 0
+ developers = Developer.find(:all, :include => 'projects', :conditions => 'developers_projects.access_level = 1', :limit => 5).to_a
+ assert_equal developers.count, Developer.instance_count
+ end
+
def test_order_on_join_table_with_include_and_limit
assert_equal 5, Developer.find(:all, :include => 'projects', :order => 'developers_projects.joined_on DESC', :limit => 5).size
end
@@ -1107,4 +1112,17 @@ class EagerAssociationTest < ActiveRecord::TestCase
Post.includes(:comments).order(nil).where(:comments => {:body => "Thank you for the welcome"}).first
end
end
+
+ test "preloading does not cache has many association subset when preloaded with a through association" do
+ author = Author.order(:id).includes(:comments_with_order_and_conditions, :posts).first
+ assert_no_queries { assert_equal 2, author.comments_with_order_and_conditions.size }
+ assert_no_queries { assert_equal 5, author.posts.size, "should not cache a subset of the association" }
+ end
+
+ test "preloading a through association twice does not reset it" do
+ members = Member.includes(:current_membership => :club).includes(:club).to_a
+ assert_no_queries {
+ assert_equal 3, members.map(&:current_membership).map(&:club).size
+ }
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index ed4475770b..869ec1e4b8 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1697,6 +1697,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [tagging], post.taggings
end
+ def test_build_with_polymotphic_has_many_does_not_allow_to_override_type_and_id
+ welcome = posts(:welcome)
+ tagging = welcome.taggings.build(:taggable_id => 99, :taggable_type => 'ShouldNotChange')
+
+ assert_equal welcome.id, tagging.taggable_id
+ assert_equal 'Post', tagging.taggable_type
+ end
+
def test_dont_call_save_callbacks_twice_on_has_many
firm = companies(:first_firm)
contract = firm.contracts.create!
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index ffd7f4efa2..277a3bddaa 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -346,6 +346,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_update_counter_caches_on_replace_association
+ post = posts(:welcome)
+ tag = post.tags.create!(:name => 'doomed')
+ tag.tagged_posts << posts(:thinking)
+
+ tag.tagged_posts = []
+ post.reload
+
+ assert_equal(post.taggings.count, post.taggings_count)
+ end
+
def test_replace_association
assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)}
@@ -851,11 +862,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- def test_assign_array_to_new_record_builds_join_records
- c = Category.new(:name => 'Fishing', :authors => [Author.first])
- assert_equal 1, c.categorizations.size
- end
-
def test_create_bang_should_raise_exception_when_join_record_has_errors
repair_validations(Categorization) do
Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 08831a42ba..31aa3788c7 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -173,6 +173,12 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_equal account, firm.account
end
+ def test_build_association_dont_create_transaction
+ assert_no_queries {
+ Firm.new.build_account
+ }
+ end
+
def test_build_and_create_should_not_happen_within_scope
pirate = pirates(:blackbeard)
scoped_count = pirate.association(:foo_bulb).scoped.where_values.count
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 073e856e5e..67b5d174aa 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -23,6 +23,8 @@ require 'models/edge'
require 'models/joke'
require 'models/bulb'
require 'models/bird'
+require 'models/car'
+require 'models/bulb'
require 'rexml/document'
require 'active_support/core_ext/exception'
require 'bcrypt'
@@ -323,6 +325,12 @@ class BasicsTest < ActiveRecord::TestCase
assert parrot.valid?
end
+ def test_default_values_are_deeply_dupped
+ company = Company.new
+ company.description << "foo"
+ assert_equal "", Company.new.description
+ end
+
def test_load
topics = Topic.find(:all, :order => 'id')
assert_equal(4, topics.size)
@@ -1293,6 +1301,16 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal({ :foo => :bar }, t.content_before_type_cast)
end
+ def test_serialized_attributes_before_type_cast_returns_unserialized_value
+ Topic.serialize :content, Hash
+
+ t = Topic.new(:content => { :foo => :bar })
+ assert_equal({ :foo => :bar }, t.attributes_before_type_cast["content"])
+ t.save!
+ t.reload
+ assert_equal({ :foo => :bar }, t.attributes_before_type_cast["content"])
+ end
+
def test_serialized_attribute_calling_dup_method
klass = Class.new(ActiveRecord::Base)
klass.table_name = "topics"
@@ -1430,6 +1448,29 @@ class BasicsTest < ActiveRecord::TestCase
Topic.serialize(:content)
end
+ def test_serialize_attribute_via_select_method_when_time_zone_available
+ ActiveRecord::Base.time_zone_aware_attributes = true
+ Topic.serialize(:content, MyObject)
+
+ myobj = MyObject.new('value1', 'value2')
+ topic = Topic.create(:content => myobj)
+
+ ActiveRecord::IdentityMap.without do
+ assert_equal(myobj, Topic.select(:content).find(topic.id).content)
+ assert_raise(ActiveModel::MissingAttributeError) { Topic.select(:id).find(topic.id).content }
+ end
+ ensure
+ ActiveRecord::Base.time_zone_aware_attributes = false
+ end
+
+ def test_serialize_attribute_can_be_serialized_in_an_integer_column
+ insures = ['life']
+ person = SerializedPerson.new(:first_name => 'David', :insures => insures)
+ assert person.save
+ person = person.reload
+ assert_equal(insures, person.insures)
+ end
+
def test_quote
author_name = "\\ \001 ' \n \\n \""
topic = Topic.create('author_name' => author_name)
@@ -1499,6 +1540,16 @@ class BasicsTest < ActiveRecord::TestCase
end
end
+ def test_dont_clear_inheritnce_column_when_setting_explicitly
+ Joke.inheritance_column = "my_type"
+ before_inherit = Joke.inheritance_column
+
+ Joke.reset_column_information
+ after_inherit = Joke.inheritance_column
+
+ assert_equal before_inherit, after_inherit unless before_inherit.blank? && after_inherit.blank?
+ end
+
def test_set_table_name_symbol_converted_to_string
Joke.table_name = :cold_jokes
assert_equal 'cold_jokes', Joke.table_name
@@ -2113,7 +2164,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_attribute_names
- assert_equal ["id", "type", "ruby_type", "firm_id", "firm_name", "name", "client_of", "rating", "account_id"],
+ assert_equal ["id", "type", "ruby_type", "firm_id", "firm_name", "name", "client_of", "rating", "account_id", "description"],
Company.attribute_names
end
@@ -2144,6 +2195,29 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal "developers/#{dev.id}-#{dev.updated_at.utc.to_s(:number)}", dev.cache_key
end
+ def test_cache_key_format_for_existing_record_with_updated_at_and_custom_cache_timestamp_format
+ return skip "Only in Ruby 1.9" if RUBY_VERSION < '1.9'
+
+ dev = CachedDeveloper.first
+ assert_equal "cached_developers/#{dev.id}-#{dev.updated_at.utc.to_s(:nsec)}", dev.cache_key
+ end
+
+ def test_cache_key_changes_when_child_touched
+ return skip "Only in Ruby 1.9" if RUBY_VERSION < '1.9'
+
+ old_timestamp_format = Car.cache_timestamp_format
+ Car.cache_timestamp_format = :nsec
+ car = Car.create
+ Bulb.create(:car => car)
+
+ key = car.cache_key
+ car.bulb.touch
+ car.reload
+ assert_not_equal key, car.cache_key
+ ensure
+ Car.cache_timestamp_format = old_timestamp_format
+ end
+
def test_cache_key_format_for_existing_record_with_nil_updated_at
dev = Developer.first
dev.update_attribute(:updated_at, nil)
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index 660098b9ad..ad2a749ab4 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -1,8 +1,9 @@
require 'cases/helper'
require 'models/post'
+require 'models/subscriber'
class EachTest < ActiveRecord::TestCase
- fixtures :posts
+ fixtures :posts, :subscribers
def setup
@posts = Post.order("id asc")
@@ -136,4 +137,24 @@ class EachTest < ActiveRecord::TestCase
assert_equal special_posts_ids, posts.map(&:id)
end
+ def test_find_in_batches_should_use_any_column_as_primary_key
+ nick_order_subscribers = Subscriber.order('nick asc')
+ start_nick = nick_order_subscribers.second.nick
+
+ subscribers = []
+ Subscriber.find_in_batches(:batch_size => 1, :start => start_nick) do |batch|
+ subscribers.concat(batch)
+ end
+
+ assert_equal nick_order_subscribers[1..-1].map(&:id), subscribers.map(&:id)
+ end
+
+ def test_find_in_batches_should_use_any_column_as_primary_key_when_start_is_not_specified
+ Subscriber.count('nick') # preheat arel's table cache
+ assert_queries(Subscriber.count + 1) do
+ Subscriber.find_each(:batch_size => 1) do |subscriber|
+ assert_kind_of Subscriber, subscriber
+ end
+ end
+ end
end
diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb
index 3652255c38..32726e8d8b 100644
--- a/activerecord/test/cases/bind_parameter_test.rb
+++ b/activerecord/test/cases/bind_parameter_test.rb
@@ -30,8 +30,7 @@ module ActiveRecord
end
def test_binds_are_logged
- # FIXME: use skip with minitest
- return unless @connection.supports_statement_cache?
+ return skip_bind_parameter_test unless supports_statement_cache?
sub = @connection.substitute_at(@pk, 0)
binds = [[@pk, 1]]
@@ -44,8 +43,7 @@ module ActiveRecord
end
def test_find_one_uses_binds
- # FIXME: use skip with minitest
- return unless @connection.supports_statement_cache?
+ return skip_bind_parameter_test unless supports_statement_cache?
Topic.find(1)
binds = [[@pk, 1]]
@@ -54,8 +52,7 @@ module ActiveRecord
end
def test_logs_bind_vars
- # FIXME: use skip with minitest
- return unless @connection.supports_statement_cache?
+ return skip_bind_parameter_test unless supports_statement_cache?
pk = Topic.columns.find { |x| x.primary }
@@ -86,5 +83,15 @@ module ActiveRecord
logger.sql event
assert_match([[pk.name, 10]].inspect, logger.debugs.first)
end
+
+ private
+
+ def skip_bind_parameter_test
+ skip('prepared statement caching is not supported')
+ end
+
+ def supports_statement_cache?
+ @connection.supports_statement_cache?
+ end
end
end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index cf1181e829..a1dc1de38d 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -1,10 +1,11 @@
require "cases/helper"
+require 'models/club'
require 'models/company'
require "models/contract"
-require 'models/topic'
require 'models/edge'
-require 'models/club'
require 'models/organization'
+require 'models/possession'
+require 'models/topic'
Company.has_many :accounts
@@ -368,6 +369,10 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 5, Account.count(:firm_id)
end
+ def test_count_with_uniq
+ assert_equal 4, Account.select(:credit_limit).uniq.count
+ end
+
def test_count_with_column_and_options_parameter
assert_equal 2, Account.count(:firm_id, :conditions => "credit_limit = 50 AND firm_id IS NOT NULL")
end
@@ -487,4 +492,26 @@ class CalculationsTest < ActiveRecord::TestCase
def test_pluck_with_qualified_column_name
assert_equal [1,2,3,4], Topic.order(:id).pluck("topics.id")
end
+
+ def test_pluck_replaces_select_clause
+ taks_relation = Topic.select([:approved, :id]).order(:id)
+ assert_equal [1,2,3,4], taks_relation.pluck(:id)
+ assert_equal [false, true, true, true], taks_relation.pluck(:approved)
+ end
+
+ def test_pluck_auto_table_name_prefix
+ c = Company.create!(:name => "test", :contracts => [Contract.new])
+ assert_equal [c.id], Company.joins(:contracts).pluck(:id)
+ end
+
+ def test_pluck_not_auto_table_name_prefix_if_column_joined
+ Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
+ assert_equal [7], Company.joins(:contracts).pluck(:developer_id).map(&:to_i)
+ end
+
+ def test_pluck_with_reserved_words
+ Possession.create!(:where => "Over There")
+
+ assert_equal ["Over There"], Possession.pluck(:where)
+ end
end
diff --git a/activerecord/test/cases/column_test.rb b/activerecord/test/cases/column_test.rb
index b1c1165bd9..800cfbdef9 100644
--- a/activerecord/test/cases/column_test.rb
+++ b/activerecord/test/cases/column_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require 'models/company'
module ActiveRecord
module ConnectionAdapters
@@ -40,12 +41,27 @@ module ActiveRecord
def test_type_cast_non_integer_to_integer
column = Column.new("field", nil, "integer")
- assert_raises(NoMethodError) do
- column.type_cast([])
- end
+ assert_nil column.type_cast([1,2])
+ assert_nil column.type_cast({1 => 2})
+ assert_nil column.type_cast((1..2))
+ end
+
+ def test_type_cast_activerecord_to_integer
+ column = Column.new("field", nil, "integer")
+ firm = Firm.create(:name => 'Apple')
+ assert_nil column.type_cast(firm)
+ end
+
+ def test_type_cast_object_without_to_i_to_integer
+ column = Column.new("field", nil, "integer")
+ assert_nil column.type_cast(Object.new)
+ end
- assert_raises(NoMethodError) do
- column.type_cast(Object.new)
+ if RUBY_VERSION > '1.9'
+ def test_type_cast_nan_and_infinity_to_integer
+ column = Column.new("field", nil, "integer")
+ assert_nil column.type_cast(Float::NAN)
+ assert_nil column.type_cast(1.0/0.0)
end
end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 4fc7e77786..9756c182b9 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -76,6 +76,8 @@ class DirtyTest < ActiveRecord::TestCase
assert pirate.created_on_changed?
assert_kind_of ActiveSupport::TimeWithZone, pirate.created_on_was
assert_equal old_created_on, pirate.created_on_was
+ pirate.created_on = old_created_on
+ assert !pirate.created_on_changed?
end
end
@@ -201,6 +203,20 @@ class DirtyTest < ActiveRecord::TestCase
end
end
+ def test_nullable_datetime_not_marked_as_changed_if_new_value_is_blank
+ in_time_zone 'Edinburgh' do
+ target = Class.new(ActiveRecord::Base)
+ target.table_name = 'topics'
+
+ topic = target.create
+ assert_equal nil, topic.written_on
+
+ topic.written_on = ""
+ assert_equal nil, topic.written_on
+ assert !topic.written_on_changed?
+ end
+ end
+
def test_integer_zero_to_string_zero_not_marked_as_changed
pirate = Pirate.new
pirate.parrot_id = 0
@@ -535,18 +551,17 @@ class DirtyTest < ActiveRecord::TestCase
end
end
- def test_setting_time_attributes_with_time_zone_field_to_same_time_should_not_be_marked_as_a_change
+ def test_datetime_attribute_can_be_updated_with_fractional_seconds
in_time_zone 'Paris' do
target = Class.new(ActiveRecord::Base)
- target.table_name = 'pirates'
+ target.table_name = 'topics'
- created_on = Time.now
+ written_on = Time.utc(2012, 12, 1, 12, 0, 0).in_time_zone('Paris')
- pirate = target.create(:created_on => created_on)
- pirate.reload # Here mysql truncate the usec value to 0
+ topic = target.create(:written_on => written_on)
+ topic.written_on += 0.3
- pirate.created_on = created_on
- assert !pirate.created_on_changed?
+ assert topic.written_on_changed?, 'Fractional second update not detected'
end
end
diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb
index b2a3cb5733..17a02f139a 100644
--- a/activerecord/test/cases/dup_test.rb
+++ b/activerecord/test/cases/dup_test.rb
@@ -113,5 +113,14 @@ module ActiveRecord
assert topic.invalid?
assert duped.valid?
end
+
+ def test_dup_with_default_scope
+ prev_default_scopes = Topic.default_scopes
+ Topic.default_scopes = [Topic.where(:approved => true)]
+ topic = Topic.new(:approved => false)
+ assert !topic.dup.approved?, "should not be overriden by default scopes"
+ ensure
+ Topic.default_scopes = prev_default_scopes
+ end
end
end
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
index cb7781f8e7..bdeb0a033c 100644
--- a/activerecord/test/cases/explain_test.rb
+++ b/activerecord/test/cases/explain_test.rb
@@ -98,6 +98,16 @@ if ActiveRecord::Base.connection.supports_explain?
assert_equal expected, base.exec_explain(queries)
end
+ def test_unsupported_connection_adapter
+ connection.stubs(:supports_explain?).returns(false)
+
+ base.logger.expects(:warn).never
+
+ with_threshold(0) do
+ Car.where(:name => 'honda').to_a
+ end
+ end
+
def test_silence_auto_explain
base.expects(:collecting_sqls_for_explain).never
base.logger.expects(:warn).never
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 77ca3b574d..7d63d76c34 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -664,6 +664,11 @@ class FinderTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::RecordNotFound) { Topic.find_by_title!("The First Topic!") }
end
+ def test_find_by_one_attribute_bang_with_blank_defined
+ blank_topic = BlankTopic.create(:title => "The Blank One")
+ assert_equal blank_topic, BlankTopic.find_by_title!("The Blank One")
+ end
+
def test_find_by_one_attribute_with_order_option
assert_equal accounts(:signals37), Account.find_by_credit_limit(50, :order => 'id')
assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :order => 'id DESC')
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index e352a55104..cf49b125d7 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -4,7 +4,7 @@ require 'config'
require 'test/unit'
require 'stringio'
-require 'mocha'
+require 'mocha/setup'
require 'active_record'
require 'active_support/dependencies'
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index 015a3ccefd..066a60f81a 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -7,6 +7,7 @@ require 'models/ship'
require 'models/legacy_thing'
require 'models/reference'
require 'models/string_key_object'
+require 'models/treasure'
class LockWithoutDefault < ActiveRecord::Base; end
@@ -20,7 +21,7 @@ class ReadonlyNameShip < Ship
end
class OptimisticLockingTest < ActiveRecord::TestCase
- fixtures :people, :legacy_things, :references, :string_key_objects
+ fixtures :people, :legacy_things, :references, :string_key_objects, :peoples_treasures
def test_non_integer_lock_existing
s1 = StringKeyObject.find("record1")
@@ -267,6 +268,15 @@ class SetLockingColumnTest < ActiveRecord::TestCase
assert_equal "omg", k.original_locking_column
end
end
+
+ def test_removing_has_and_belongs_to_many_associations_upon_destroy
+ p = RichPerson.create! :first_name => 'Jon'
+ p.treasures.create!
+ assert !p.treasures.empty?
+ p.destroy
+ assert p.treasures.empty?
+ assert RichPerson.connection.select_all("SELECT * FROM peoples_treasures WHERE rich_person_id = 1").empty?
+ end
end
class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index e24a5ca5aa..10ee86de4a 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -7,6 +7,19 @@ class LogSubscriberTest < ActiveRecord::TestCase
include ActiveSupport::LogSubscriber::TestHelper
include ActiveSupport::BufferedLogger::Severity
+ class TestDebugLogSubscriber < ActiveRecord::LogSubscriber
+ attr_reader :debugs
+
+ def initialize
+ @debugs = []
+ super
+ end
+
+ def debug message
+ @debugs << message
+ end
+ end
+
fixtures :posts
def setup
@@ -32,18 +45,7 @@ class LogSubscriberTest < ActiveRecord::TestCase
def test_schema_statements_are_ignored
event = Struct.new(:duration, :payload)
- logger = Class.new(ActiveRecord::LogSubscriber) {
- attr_accessor :debugs
-
- def initialize
- @debugs = []
- super
- end
-
- def debug message
- @debugs << message
- end
- }.new
+ logger = TestDebugLogSubscriber.new
assert_equal 0, logger.debugs.length
logger.sql(event.new(0, { :sql => 'hi mom!' }))
@@ -56,6 +58,14 @@ class LogSubscriberTest < ActiveRecord::TestCase
assert_equal 2, logger.debugs.length
end
+ def test_ignore_binds_payload_with_nil_column
+ event = Struct.new(:duration, :payload)
+
+ logger = TestDebugLogSubscriber.new
+ logger.sql(event.new(0, :sql => 'hi mom!', :binds => [[nil, 1]]))
+ assert_equal 1, logger.debugs.length
+ end
+
def test_basic_query_logging
Developer.all
wait
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index 0ab4f30363..ac84306eae 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -212,14 +212,14 @@ class MethodScopingTest < ActiveRecord::TestCase
table = VerySpecialComment.arel_table
relation = VerySpecialComment.scoped
relation.where_values << table[:id].not_eq(1)
- assert_equal({:type => "VerySpecialComment"}, relation.send(:scope_for_create))
+ assert_equal({'type' => "VerySpecialComment"}, relation.send(:scope_for_create))
end
def test_scoped_create
new_comment = nil
VerySpecialComment.send(:with_scope, :create => { :post_id => 1 }) do
- assert_equal({:post_id => 1, :type => 'VerySpecialComment' }, VerySpecialComment.scoped.send(:scope_for_create))
+ assert_equal({'post_id' => 1, 'type' => 'VerySpecialComment' }, VerySpecialComment.scoped.send(:scope_for_create))
new_comment = VerySpecialComment.create :body => "Wonderful world"
end
@@ -228,7 +228,7 @@ class MethodScopingTest < ActiveRecord::TestCase
def test_scoped_create_with_join_and_merge
Comment.where(:body => "but Who's Buying?").joins(:post).merge(Post.where(:body => 'Peace Sells...')).with_scope do
- assert_equal({:body => "but Who's Buying?"}, Comment.scoped.scope_for_create)
+ assert_equal({'body' => "but Who's Buying?"}, Comment.scoped.scope_for_create)
end
end
@@ -441,7 +441,7 @@ class NestedScopingTest < ActiveRecord::TestCase
comment = nil
Comment.send(:with_scope, :create => { :post_id => 1}) do
Comment.send(:with_scope, :create => { :post_id => 2}) do
- assert_equal({:post_id => 2}, Comment.scoped.send(:scope_for_create))
+ assert_equal({'post_id' => 2}, Comment.scoped.send(:scope_for_create))
comment = Comment.create :body => "Hey guys, nested scopes are broken. Please fix!"
end
end
@@ -453,7 +453,7 @@ class NestedScopingTest < ActiveRecord::TestCase
Comment.send(:with_scope, :create => { :body => "Hey guys, nested scopes are broken. Please fix!" }) do
Comment.send(:with_exclusive_scope, :create => { :post_id => 1 }) do
- assert_equal({:post_id => 1}, Comment.scoped.send(:scope_for_create))
+ assert_equal({'post_id' => 1}, Comment.scoped.send(:scope_for_create))
assert_blank Comment.new.body
comment = Comment.create :body => "Hey guys"
end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index f0c55b1535..7f0d921545 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -61,7 +61,7 @@ if ActiveRecord::Base.connection.supports_migrations?
ActiveRecord::Base.connection.initialize_schema_migrations_table
ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
- %w(things awesome_things prefix_things_suffix prefix_awesome_things_suffix).each do |table|
+ %w(things awesome_things prefix_things_suffix p_awesome_things_s).each do |table|
Thing.connection.drop_table(table) rescue nil
end
Thing.reset_column_information
@@ -874,8 +874,6 @@ if ActiveRecord::Base.connection.supports_migrations?
end
def test_remove_column_with_array_as_an_argument_is_deprecated
- return skip "remove_column with array as argument is not supported with OracleAdapter" if current_adapter? :OracleAdapter
-
ActiveRecord::Base.connection.create_table(:hats) do |table|
table.column :hat_name, :string, :limit => 100
table.column :hat_size, :integer
@@ -886,7 +884,21 @@ if ActiveRecord::Base.connection.supports_migrations?
Person.connection.remove_column("hats", ["hat_name", "hat_size"])
end
ensure
- ActiveRecord::Base.connection.drop_table(:hats) rescue nil
+ ActiveRecord::Base.connection.drop_table(:hats)
+ end
+
+ def test_removing_and_renaming_column_preserves_custom_primary_key
+ ActiveRecord::Base.connection.create_table "my_table", :primary_key => "my_table_id", :force => true do |t|
+ t.integer "col_one"
+ t.string "col_two", :limit => 128, :null => false
+ end
+
+ ActiveRecord::Base.connection.remove_column("my_table", "col_two")
+ ActiveRecord::Base.connection.rename_column("my_table", "col_one", "col_three")
+
+ assert_equal 'my_table_id', ActiveRecord::Base.connection.primary_key('my_table')
+ ensure
+ ActiveRecord::Base.connection.drop_table(:my_table) rescue nil
end
def test_change_type_of_not_null_column
@@ -1051,6 +1063,18 @@ if ActiveRecord::Base.connection.supports_migrations?
Person.connection.remove_column("people", "administrator") rescue nil
end
+ def test_change_column_with_custom_index_name
+ Person.connection.add_column "people", "category", :string, :default => 'human'
+ Person.connection.add_index :people, :category, :name => 'people_categories_idx'
+
+ assert_equal ['people_categories_idx'], Person.connection.indexes('people').map(&:name)
+ Person.connection.change_column "people", "category", :string, :null => false, :default => 'article'
+
+ assert_equal ['people_categories_idx'], Person.connection.indexes('people').map(&:name)
+ ensure
+ Person.connection.remove_column("people", "category") rescue nil
+ end
+
def test_change_column_default
Person.connection.change_column_default "people", "first_name", "Tester"
Person.reset_column_information
@@ -1430,6 +1454,12 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
+ def test_finds_migrations_in_numbered_directory
+ migrations = ActiveRecord::Migrator.migrations [MIGRATIONS_ROOT + '/10_urban']
+ assert_equal 9, migrations[0].version
+ assert_equal 'AddExpressions', migrations[0].name
+ end
+
def test_dump_schema_information_outputs_lexically_ordered_versions
migration_path = MIGRATIONS_ROOT + '/valid_with_timestamps'
ActiveRecord::Migrator.run(:up, migration_path, 20100301010101)
@@ -1615,8 +1645,8 @@ if ActiveRecord::Base.connection.supports_migrations?
def test_rename_table_with_prefix_and_suffix
assert !Thing.table_exists?
- ActiveRecord::Base.table_name_prefix = 'prefix_'
- ActiveRecord::Base.table_name_suffix = '_suffix'
+ ActiveRecord::Base.table_name_prefix = 'p_'
+ ActiveRecord::Base.table_name_suffix = '_s'
Thing.reset_table_name
Thing.reset_sequence_name
WeNeedThings.up
@@ -1625,7 +1655,7 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_equal "hello world", Thing.find(:first).content
RenameThings.up
- Thing.table_name = "prefix_awesome_things_suffix"
+ Thing.table_name = "p_awesome_things_s"
assert_equal "hello world", Thing.find(:first).content
ensure
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 16b1eb040e..85b9d3c1a1 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -172,6 +172,17 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
man.interests_attributes = [{:id => interest.id, :topic => 'gardening'}]
assert_equal man.interests.first.topic, man.interests[0].topic
end
+
+ def test_accepts_nested_attributes_for_can_be_overridden_in_subclasses
+ Pirate.accepts_nested_attributes_for(:parrot)
+
+ mean_pirate_class = Class.new(Pirate) do
+ accepts_nested_attributes_for :parrot
+ end
+ mean_pirate = mean_pirate_class.new
+ mean_pirate.parrot_attributes = { :name => "James" }
+ assert_equal "James", mean_pirate.parrot.name
+ end
end
class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index a35cb3c4a5..db1ba3549f 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -394,7 +394,6 @@ class PersistencesTest < ActiveRecord::TestCase
def test_update_attribute_with_one_updated
t = Topic.first
- title = t.title
t.update_attribute(:title, 'super_title')
assert_equal 'super_title', t.title
assert !t.changed?, "topic should not have changed"
@@ -492,7 +491,7 @@ class PersistencesTest < ActiveRecord::TestCase
def test_update_column_with_one_changed_and_one_updated
t = Topic.order('id').limit(1).first
- title, author_name = t.title, t.author_name
+ author_name = t.author_name
t.author_name = 'John'
t.update_column(:title, 'super_title')
assert_equal 'John', t.author_name
@@ -519,6 +518,14 @@ class PersistencesTest < ActiveRecord::TestCase
assert return_value
end
+ def test_update_column_with_default_scope
+ developer = DeveloperCalledDavid.first
+ developer.name = 'John'
+ developer.save!
+
+ assert developer.update_column(:name, 'Will'), 'did not update record due to default scope'
+ end
+
def test_update_attributes
topic = Topic.find(1)
assert !topic.approved?
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index dd881f8230..dfc6ea2457 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -173,6 +173,17 @@ class QueryCacheTest < ActiveRecord::TestCase
assert_queries(2) { task.lock!; task.lock! }
end
end
+
+ def test_cache_is_available_when_connection_is_connected
+ conf = ActiveRecord::Base.configurations
+
+ ActiveRecord::Base.configurations = {}
+ Task.cache do
+ assert_queries(1) { Task.find(1); Task.find(1) }
+ end
+ ensure
+ ActiveRecord::Base.configurations = conf
+ end
end
class QueryCacheExpiryTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index babeaec5f6..80ee74e41e 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -122,35 +122,35 @@ module ActiveRecord
def test_quote_float
float = 1.2
assert_equal float.to_s, @quoter.quote(float, nil)
- assert_equal float.to_s, @quoter.quote(float, FakeColumn.new(:float))
+ assert_equal float.to_s, @quoter.quote(float, Object.new)
end
def test_quote_fixnum
fixnum = 1
assert_equal fixnum.to_s, @quoter.quote(fixnum, nil)
- assert_equal fixnum.to_s, @quoter.quote(fixnum, FakeColumn.new(:integer))
+ assert_equal fixnum.to_s, @quoter.quote(fixnum, Object.new)
end
def test_quote_bignum
bignum = 1 << 100
assert_equal bignum.to_s, @quoter.quote(bignum, nil)
- assert_equal bignum.to_s, @quoter.quote(bignum, FakeColumn.new(:integer))
+ assert_equal bignum.to_s, @quoter.quote(bignum, Object.new)
end
def test_quote_bigdecimal
bigdec = BigDecimal.new((1 << 100).to_s)
assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, nil)
- assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, FakeColumn.new(:decimal))
+ assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, Object.new)
end
def test_dates_and_times
@quoter.extend(Module.new { def quoted_date(value) 'lol' end })
assert_equal "'lol'", @quoter.quote(Date.today, nil)
- assert_equal "'lol'", @quoter.quote(Date.today, FakeColumn.new(:date))
+ assert_equal "'lol'", @quoter.quote(Date.today, Object.new)
assert_equal "'lol'", @quoter.quote(Time.now, nil)
- assert_equal "'lol'", @quoter.quote(Time.now, FakeColumn.new(:time))
+ assert_equal "'lol'", @quoter.quote(Time.now, Object.new)
assert_equal "'lol'", @quoter.quote(DateTime.now, nil)
- assert_equal "'lol'", @quoter.quote(DateTime.now, FakeColumn.new(:datetime))
+ assert_equal "'lol'", @quoter.quote(DateTime.now, Object.new)
end
def test_crazy_object
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index ba9df1651b..80158332f9 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -35,30 +35,5 @@ module ActiveRecord
def test_where_with_empty_hash_and_no_foreign_key
assert_equal 0, Edge.where(:sink => {}).count
end
-
- def test_where_with_integer_for_string_column
- count = Post.where(:title => 0).count
- assert_equal 0, count
- end
-
- def test_where_with_float_for_string_column
- count = Post.where(:title => 0.0).count
- assert_equal 0, count
- end
-
- def test_where_with_boolean_for_string_column
- count = Post.where(:title => false).count
- assert_equal 0, count
- end
-
- def test_where_with_decimal_for_string_column
- count = Post.where(:title => BigDecimal.new('0')).count
- assert_equal 0, count
- end
-
- def test_where_with_duration_for_string_column
- count = Post.where(:title => 0.seconds).count
- assert_equal 0, count
- end
end
end
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index aec2487aee..f33e765c59 100644
--- a/activerecord/test/cases/relation_scoping_test.rb
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -380,19 +380,19 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_default_scope_with_inheritance
wheres = InheritedPoorDeveloperCalledJamis.scoped.where_values_hash
assert_equal "Jamis", wheres[:name]
- assert_equal Arel.sql("50000"), wheres[:salary]
+ assert_equal 50000, wheres[:salary]
end
def test_default_scope_with_module_includes
wheres = ModuleIncludedPoorDeveloperCalledJamis.scoped.where_values_hash
assert_equal "Jamis", wheres[:name]
- assert_equal Arel.sql("50000"), wheres[:salary]
+ assert_equal 50000, wheres[:salary]
end
def test_default_scope_with_multiple_calls
wheres = MultiplePoorDeveloperCalledJamis.scoped.where_values_hash
assert_equal "Jamis", wheres[:name]
- assert_equal Arel.sql("50000"), wheres[:salary]
+ assert_equal 50000, wheres[:salary]
end
def test_method_scope
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 7a75a8436b..6efdeac3d9 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -71,7 +71,7 @@ module ActiveRecord
def test_has_values
relation = Relation.new Post, Post.arel_table
relation.where_values << relation.table[:id].eq(10)
- assert_equal({:id => 10}, relation.where_values_hash)
+ assert_equal({'id' => 10}, relation.where_values_hash)
end
def test_values_wrong_table
@@ -101,7 +101,7 @@ module ActiveRecord
def test_create_with_value
relation = Relation.new Post, Post.arel_table
- hash = { :hello => 'world' }
+ hash = { 'hello' => 'world' }
relation.create_with_value = hash
assert_equal hash, relation.scope_for_create
end
@@ -110,7 +110,7 @@ module ActiveRecord
relation = Relation.new Post, Post.arel_table
relation.where_values << relation.table[:id].eq(10)
relation.create_with_value = {:hello => 'world'}
- assert_equal({:hello => 'world', :id => 10}, relation.scope_for_create)
+ assert_equal({'hello' => 'world', 'id' => 10}, relation.scope_for_create)
end
# FIXME: is this really wanted or expected behavior?
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index b24df47efd..98fe6b7611 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -332,6 +332,13 @@ class RelationTest < ActiveRecord::TestCase
end
end
+ def test_preload_applies_to_all_chained_preloaded_scopes
+ assert_queries(3) do
+ post = Post.with_tags.with_comments.first
+ assert post
+ end
+ end
+
def test_find_with_included_associations
assert_queries(2) do
posts = Post.includes(:comments).order('posts.id')
@@ -1185,4 +1192,18 @@ class RelationTest < ActiveRecord::TestCase
end
assert_equal ['Foo', 'Foo'], query.uniq(true).uniq(false).map(&:name)
end
+
+ test 'group with select and includes' do
+ authors_count = Post.select('author_id, COUNT(author_id) AS num_posts').
+ group('author_id').order('author_id').includes(:author).to_a
+
+ assert_no_queries do
+ result = authors_count.map do |post|
+ [post.num_posts.to_i, post.author.try(:name)]
+ end
+
+ expected = [[1, nil], [5, "David"], [3, "Mary"], [2, "Bob"]]
+ assert_equal expected, result
+ end
+ end
end
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index 8708117129..0f9cb10f0f 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -22,6 +22,14 @@ end
class Thaumaturgist < IneptWizard
end
+class ReplyTitle; end
+
+class ReplyWithTitleObject < Reply
+ validates_uniqueness_of :content, :scope => :title
+
+ def title; ReplyTitle.new; end
+end
+
class UniquenessValidationTest < ActiveRecord::TestCase
fixtures :topics, 'warehouse-things', :developers
@@ -80,6 +88,14 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert r3.valid?, "Saving r3"
end
+ def test_validate_uniqueness_with_composed_attribute_scope
+ r1 = ReplyWithTitleObject.create "title" => "r1", "content" => "hello world"
+ assert r1.valid?, "Saving r1"
+
+ r2 = ReplyWithTitleObject.create "title" => "r1", "content" => "hello world"
+ assert !r2.valid?, "Saving r2 first time"
+ end
+
def test_validate_uniqueness_scoped_to_defining_class
t = Topic.create("title" => "What, me worry?")
diff --git a/activerecord/test/fixtures/peoples_treasures.yml b/activerecord/test/fixtures/peoples_treasures.yml
new file mode 100644
index 0000000000..a72b190d0c
--- /dev/null
+++ b/activerecord/test/fixtures/peoples_treasures.yml
@@ -0,0 +1,3 @@
+michael_diamond:
+ rich_person_id: <%= ActiveRecord::Fixtures.identify(:michael) %>
+ treasure_id: <%= ActiveRecord::Fixtures.identify(:diamond) %>
diff --git a/activerecord/test/migrations/10_urban/9_add_expressions.rb b/activerecord/test/migrations/10_urban/9_add_expressions.rb
new file mode 100644
index 0000000000..79a342e574
--- /dev/null
+++ b/activerecord/test/migrations/10_urban/9_add_expressions.rb
@@ -0,0 +1,11 @@
+class AddExpressions < ActiveRecord::Migration
+ def self.up
+ create_table("expressions") do |t|
+ t.column :expression, :string
+ end
+ end
+
+ def self.down
+ drop_table "expressions"
+ end
+end
diff --git a/activerecord/test/models/bulb.rb b/activerecord/test/models/bulb.rb
index 888afc7604..ab3caa3f79 100644
--- a/activerecord/test/models/bulb.rb
+++ b/activerecord/test/models/bulb.rb
@@ -1,6 +1,6 @@
class Bulb < ActiveRecord::Base
default_scope where(:name => 'defaulty')
- belongs_to :car
+ belongs_to :car, :touch => true
attr_protected :car_id, :frickinawesome
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 4dc9fff9fd..f8b758b3c2 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -63,6 +63,15 @@ class Developer < ActiveRecord::Base
self.all
end
end
+
+ after_find :track_instance_count
+ cattr_accessor :instance_count
+
+ def track_instance_count
+ self.class.instance_count ||= 0
+ self.class.instance_count += 1
+ end
+ private :track_instance_count
end
class AuditLog < ActiveRecord::Base
@@ -236,3 +245,8 @@ class ThreadsafeDeveloper < ActiveRecord::Base
limit(1)
end
end
+
+class CachedDeveloper < ActiveRecord::Base
+ self.table_name = "developers"
+ self.cache_timestamp_format = :nsec
+end
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index 5b92227f4a..07c529d685 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -86,3 +86,30 @@ class TightPerson < ActiveRecord::Base
end
class TightDescendant < TightPerson; end
+
+class RichPerson < ActiveRecord::Base
+ self.table_name = 'people'
+
+ has_and_belongs_to_many :treasures, :join_table => 'peoples_treasures'
+end
+
+class Insure
+ INSURES = %W{life annuality}
+
+ def self.load mask
+ INSURES.select do |insure|
+ (1 << INSURES.index(insure)) & mask.to_i > 0
+ end
+ end
+
+ def self.dump insures
+ numbers = insures.map { |insure| INSURES.index(insure) }
+ numbers.inject(0) { |sum, n| sum + (1 << n) }
+ end
+end
+
+class SerializedPerson < ActiveRecord::Base
+ self.table_name = 'people'
+
+ serialize :insures, Insure
+end
diff --git a/activerecord/test/models/possession.rb b/activerecord/test/models/possession.rb
new file mode 100644
index 0000000000..ddf759113b
--- /dev/null
+++ b/activerecord/test/models/possession.rb
@@ -0,0 +1,3 @@
+class Possession < ActiveRecord::Base
+ self.table_name = 'having'
+end
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 9aa02fa18f..3cfd1a0f76 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -76,6 +76,9 @@ class Post < ActiveRecord::Base
end
end
+ scope :with_comments, preload(:comments)
+ scope :with_tags, preload(:taggings)
+
has_many :interpolated_taggings, :class_name => 'Tagging', :as => :taggable, :conditions => proc { "1 = #{1}" }
has_many :interpolated_tags, :through => :taggings
has_many :interpolated_tags_2, :through => :interpolated_taggings, :source => :tag
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 1a1a18166a..5166fefe81 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -112,6 +112,12 @@ class ImportantTopic < Topic
serialize :important, Hash
end
+class BlankTopic < Topic
+ def blank?
+ true
+ end
+end
+
module Web
class Topic < ActiveRecord::Base
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id", :class_name => 'Web::Reply'
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 34e24b5a2e..b2c655ddcd 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -132,5 +132,10 @@ _SQL
_SQL
rescue #This version of PostgreSQL either has no XML support or is was not compiled with XML support: skipping table
end
+
+ create_table :limitless_fields, :force => true do |t|
+ t.binary :binary, :limit => 100_000
+ t.text :text, :limit => 100_000
+ end
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index f04dc72cdd..ab62fb82b6 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -112,6 +112,7 @@ ActiveRecord::Schema.define do
t.string :name
t.integer :engines_count
t.integer :wheels_count
+ t.timestamps
end
create_table :categories, :force => true do |t|
@@ -175,6 +176,7 @@ ActiveRecord::Schema.define do
t.integer :client_of
t.integer :rating, :default => 1
t.integer :account_id
+ t.string :description, :default => ""
end
add_index :companies, [:firm_id, :type, :rating, :ruby_type], :name => "company_index"
@@ -280,6 +282,10 @@ ActiveRecord::Schema.define do
t.string :info
end
+ create_table :having, :force => true do |t|
+ t.string :where
+ end
+
create_table :guids, :force => true do |t|
t.column :key, :string
end
@@ -478,9 +484,15 @@ ActiveRecord::Schema.define do
t.integer :followers_count, :default => 0
t.references :best_friend
t.references :best_friend_of
+ t.integer :insures, :null => false, :default => 0
t.timestamps
end
+ create_table :peoples_treasures, :id => false, :force => true do |t|
+ t.column :rich_person_id, :integer
+ t.column :treasure_id, :integer
+ end
+
create_table :pets, :primary_key => :pet_id ,:force => true do |t|
t.string :name
t.integer :owner_id, :integer
@@ -518,8 +530,6 @@ ActiveRecord::Schema.define do
create_table :price_estimates, :force => true do |t|
t.string :estimate_of_type
t.integer :estimate_of_id
- t.string :thing_type
- t.integer :thing_id
t.integer :price
end
diff --git a/activeresource/CHANGELOG.md b/activeresource/CHANGELOG.md
index 8d251bdce2..77419bb904 100644
--- a/activeresource/CHANGELOG.md
+++ b/activeresource/CHANGELOG.md
@@ -1,21 +1,47 @@
-## Rails 3.2.10 ##
+## unreleased ##
+
+* No changes.
+
+
+## Rails 3.2.13 (Mar 18, 2013) ##
+
+* No changes.
+
+## Rails 3.2.12 (Feb 11, 2013) ##
+
+* No changes.
+
+
+## Rails 3.2.11 (Jan 8, 2013) ##
+
+* No changes.
+
+
+## Rails 3.2.10 (Jan 2, 2013) ##
+
+* No changes.
+
## Rails 3.2.9 (Nov 12, 2012) ##
* No changes.
+
## Rails 3.2.8 (Aug 9, 2012) ##
* No changes.
+
## Rails 3.2.7 (Jul 26, 2012) ##
* No changes.
+
## Rails 3.2.6 (Jun 12, 2012) ##
* No changes.
+
## Rails 3.2.5 (Jun 1, 2012) ##
* No changes.
diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb
index 500da6c137..6c3ee00be6 100644
--- a/activeresource/lib/active_resource/version.rb
+++ b/activeresource/lib/active_resource/version.rb
@@ -2,7 +2,7 @@ module ActiveResource
module VERSION #:nodoc:
MAJOR = 3
MINOR = 2
- TINY = 11
+ TINY = 13
PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb
index 9c1e9a526d..583fd2afb7 100644
--- a/activeresource/test/abstract_unit.rb
+++ b/activeresource/test/abstract_unit.rb
@@ -77,19 +77,6 @@ def setup_response
}]
}
}.to_json
- # - resource with yaml array of strings; for ARs using serialize :bar, Array
- @marty = <<-eof.strip
- <?xml version=\"1.0\" encoding=\"UTF-8\"?>
- <person>
- <id type=\"integer\">5</id>
- <name>Marty</name>
- <colors type=\"yaml\">---
- - \"red\"
- - \"green\"
- - \"blue\"
- </colors>
- </person>
- eof
@startup_sound = {
:sound => {
@@ -101,7 +88,6 @@ def setup_response
mock.get "/people/1.json", {}, @matz
mock.get "/people/1.xml", {}, @matz_xml
mock.get "/people/2.xml", {}, @david
- mock.get "/people/5.xml", {}, @marty
mock.get "/people/Greg.json", {}, @greg
mock.get "/people/6.json", {}, @joe
mock.get "/people/4.json", { 'key' => 'value' }, nil, 404
diff --git a/activeresource/test/cases/base_test.rb b/activeresource/test/cases/base_test.rb
index 7b42f64a35..983f0541a8 100644
--- a/activeresource/test/cases/base_test.rb
+++ b/activeresource/test/cases/base_test.rb
@@ -10,7 +10,7 @@ require "fixtures/subscription_plan"
require 'active_support/json'
require 'active_support/ordered_hash'
require 'active_support/core_ext/hash/conversions'
-require 'mocha'
+require 'mocha/setup'
class BaseTest < Test::Unit::TestCase
def setup
@@ -1077,19 +1077,6 @@ class BaseTest < Test::Unit::TestCase
end
end
- def test_load_yaml_array
- assert_nothing_raised do
- Person.format = :xml
- marty = Person.find(5)
- assert_equal 3, marty.colors.size
- marty.colors.each do |color|
- assert_kind_of String, color
- end
- end
- ensure
- Person.format = :json
- end
-
def test_with_custom_formatter
addresses = [{ :id => "1", :street => "1 Infinite Loop", :city => "Cupertino", :state => "CA" }].to_xml(:root => :addresses)
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index ec198059fb..649766cc9b 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,4 +1,44 @@
-## Rails 3.2.10 (Jan 8, 2012) ##
+## unreleased ##
+
+* Fix `ActiveSupport::TimeZone.parse` when time is at a local DST jump.
+ Fixes #9678.
+
+ *Andrew White*
+
+
+## Rails 3.2.13 (Mar 18, 2013) ##
+
+
+* Fix DateTime comparison with DateTime::Infinity object.
+
+ *Dan Kubb*
+
+* Remove surrogate unicode character encoding from ActiveSupport::JSON.encode
+ The encoding scheme was broken for unicode characters outside the basic
+ multilingual plane; since json is assumed to be UTF-8, and we already force the
+ encoding to UTF-8 simply pass through the un-encoded characters.
+
+ *Brett Carter*
+
+* Fix mocha v0.13.0 compatibility. *James Mead*
+
+* `#as_json` isolates options when encoding a hash. [Backport #8185]
+ Fix #8182
+
+ *Yves Senn*
+
+* Handle the possible Permission Denied errors atomic.rb might trigger due to
+ its chown and chmod calls. [Backport #8027]
+
+ *Daniele Sluijters*
+
+
+## Rails 3.2.12 (Feb 11, 2013) ##
+
+* No changes.
+
+
+## Rails 3.2.11 (Jan 8, 2012) ##
* Hash.from_xml raises when it encounters type="symbol" or type="yaml".
Use Hash.from_trusted_xml to parse this XML.
@@ -7,6 +47,12 @@
*Jeremy Kemper*
+
+## Rails 3.2.10 (Jan 2, 2013) ##
+
+* No changes.
+
+
## Rails 3.2.9 (Nov 12, 2012) ##
* Add logger.push_tags and .pop_tags to complement logger.tagged:
@@ -25,6 +71,7 @@
* Add %:z and %::z format string support to ActiveSupport::TimeWithZone#strftime. [fixes #6962] *kennyj*
+
## Rails 3.2.8 (Aug 9, 2012) ##
* Fix ActiveSupport integration with Mocha > 0.12.1. *Mike Gunderloy*
@@ -33,6 +80,7 @@
* ERB::Util.html_escape now escapes single quotes. *Santiago Pastorino*
+
## Rails 3.2.7 (Jul 26, 2012) ##
* Hash#fetch(fetch) is not the same as doing hash[key]
@@ -45,10 +93,12 @@
* bump AS deprecation_horizon to 4.0
+
## Rails 3.2.6 (Jun 12, 2012) ##
* No changes.
+
## Rails 3.2.5 (Jun 1, 2012) ##
* ActiveSupport::JSON::Variable is deprecated. Define your own #as_json and #encode_json methods
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index e6fc7bf571..fb3575b865 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -18,6 +18,6 @@ Gem::Specification.new do |s|
s.rdoc_options.concat ['--encoding', 'UTF-8']
- s.add_dependency('i18n', '~> 0.6')
+ s.add_dependency('i18n', '~> 0.6', '>= 0.6.4')
s.add_dependency('multi_json', '~> 1.0')
end
diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb
index ae1d0633ce..677f7c0bb0 100644
--- a/activesupport/lib/active_support/concern.rb
+++ b/activesupport/lib/active_support/concern.rb
@@ -6,7 +6,9 @@ module ActiveSupport
# module M
# def self.included(base)
# base.extend ClassMethods
- # scope :disabled, where(:disabled => true)
+ # base.class_eval do
+ # scope :disabled, where(:disabled => true)
+ # end
# end
#
# module ClassMethods
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 b759dcc824..0481bd2195 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -138,6 +138,6 @@ class DateTime
# Layers additional behavior on DateTime#<=> so that Time and ActiveSupport::TimeWithZone instances can be compared with a DateTime
def <=>(other)
- super other.to_datetime
+ super other.kind_of?(Infinity) ? other : other.to_datetime
end
end
diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb
index fc3277f4d2..b0daf6db9e 100644
--- a/activesupport/lib/active_support/core_ext/file/atomic.rb
+++ b/activesupport/lib/active_support/core_ext/file/atomic.rb
@@ -36,7 +36,12 @@ class File
FileUtils.mv(temp_file.path, file_name)
# Set correct permissions on new file
- chown(old_stat.uid, old_stat.gid, file_name)
- chmod(old_stat.mode, file_name)
+ begin
+ chown(old_stat.uid, old_stat.gid, file_name)
+ # This operation will affect filesystem ACL's
+ chmod(old_stat.mode, file_name)
+ rescue Errno::EPERM
+ # Changing file ownership failed, moving on.
+ end
end
end
diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
index 7d54c9fae6..e5042c6c18 100644
--- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
+++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -1,7 +1,6 @@
require 'active_support/hash_with_indifferent_access'
class Hash
-
# Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
#
# {:a => 1}.with_indifferent_access["a"] # => 1
diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb
index 0484d8e5d8..a983cae39e 100644
--- a/activesupport/lib/active_support/core_ext/hash/slice.rb
+++ b/activesupport/lib/active_support/core_ext/hash/slice.rb
@@ -13,7 +13,7 @@ class Hash
# valid_keys = [:mass, :velocity, :time]
# search(options.slice(*valid_keys))
def slice(*keys)
- keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
+ keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
hash = self.class.new
keys.each { |k| hash[k] = self[k] if has_key?(k) }
hash
@@ -23,7 +23,7 @@ class Hash
# Returns a hash contained the removed key/value pairs
# {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d => 4}
def slice!(*keys)
- keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
+ keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
omit = slice(*self.keys - keys)
hash = slice(*keys)
replace(hash)
diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
index 526b8378a5..84986dff3c 100644
--- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
@@ -1,4 +1,6 @@
require 'rbconfig'
+require 'stringio'
+
module Kernel
# Sets $VERBOSE to nil for the duration of the block and back to its original value afterwards.
#
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 34d4d88bd3..9146d82bd8 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -217,7 +217,7 @@ class Time
# Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
def end_of_day
- change(:hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
+ change(:hour => 23, :min => 59, :sec => 59, :usec => Rational(999999999, 1000))
end
# Returns a new Time representing the start of the hour (x:00)
@@ -228,11 +228,7 @@ class Time
# 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
- )
+ change(:min => 59, :sec => 59, :usec => Rational(999999999, 1000))
end
# Returns a new Time representing the start of the month (1st of the month, 0:00)
@@ -246,7 +242,7 @@ class Time
def end_of_month
#self - ((self.mday-1).days + self.seconds_since_midnight)
last_day = ::Time.days_in_month(month, year)
- change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
+ change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => Rational(999999999, 1000))
end
alias :at_end_of_month :end_of_month
@@ -270,7 +266,7 @@ class Time
# Returns a new Time representing the end of the year (end of the 31st of december)
def end_of_year
- change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
+ change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59, :usec => Rational(999999999, 1000))
end
alias :at_end_of_year :end_of_year
diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb
index 49ac18d245..d180e1e588 100644
--- a/activesupport/lib/active_support/core_ext/time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/time/conversions.rb
@@ -13,6 +13,8 @@ class Time
:rfc822 => lambda { |time| time.strftime("%a, %d %b %Y %H:%M:%S #{time.formatted_offset(false)}") }
}
+ DATE_FORMATS[:nsec] = '%Y%m%d%H%M%S%9N' if RUBY_VERSION >= '1.9'
+
# Converts to a formatted string. See DATE_FORMATS for builtin formats.
#
# This method is aliased to <tt>to_s</tt>.
diff --git a/activesupport/lib/active_support/core_ext/time/marshal.rb b/activesupport/lib/active_support/core_ext/time/marshal.rb
index 457d3f5b62..ce5948d126 100644
--- a/activesupport/lib/active_support/core_ext/time/marshal.rb
+++ b/activesupport/lib/active_support/core_ext/time/marshal.rb
@@ -20,7 +20,7 @@ if !Marshal.load(Marshal.dump(Time.now.utc)).utc?
def _dump(*args)
obj = dup
obj.instance_variable_set('@marshal_with_utc_coercion', utc?)
- obj._dump_without_utc_flag(*args)
+ obj.send :_dump_without_utc_flag, *args
end
end
end
@@ -51,7 +51,7 @@ if Time.local(2010).zone != Marshal.load(Marshal.dump(Time.local(2010))).zone
def _dump(*args)
obj = dup
obj.instance_variable_set('@_zone', zone)
- obj._dump_without_zone(*args)
+ obj.send :_dump_without_zone, *args
end
end
end
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 9e7cb76307..9dc93de5a9 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -6,7 +6,7 @@ require 'active_support/core_ext/hash/keys'
module ActiveSupport
class HashWithIndifferentAccess < Hash
-
+
# Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class.
def extractable_options?
true
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index 91e4f07c6c..a50e6524c6 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -61,7 +61,7 @@ module ActiveSupport
# hashes and arrays need to get encoder in the options, so that they can detect circular references
options.merge(:encoder => self)
else
- options
+ options.dup
end
end
@@ -122,13 +122,7 @@ module ActiveSupport
if string.respond_to?(:force_encoding)
string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
end
- json = string.
- gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
- gsub(/([\xC0-\xDF][\x80-\xBF]|
- [\xE0-\xEF][\x80-\xBF]{2}|
- [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
- s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&')
- }
+ json = string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] }
json = %("#{json}")
json.force_encoding(::Encoding::UTF_8) if json.respond_to?(:force_encoding)
json
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index 8d6c27e381..573736ede7 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -10,7 +10,7 @@ require 'active_support/core_ext/kernel/reporting'
module ActiveSupport
class TestCase < ::Test::Unit::TestCase
- if defined? MiniTest
+ if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
Assertion = MiniTest::Assertion
alias_method :method_name, :name if method_defined? :name
alias_method :method_name, :__name__ if method_defined? :__name__
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index 6b29ba4c10..77c04758ba 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -12,8 +12,8 @@ module ActiveSupport
end
class ProxyTestResult
- def initialize
- @calls = []
+ def initialize(calls = [])
+ @calls = calls
end
def add_error(e)
@@ -27,6 +27,14 @@ module ActiveSupport
end
end
+ def marshal_dump
+ @calls
+ end
+
+ def marshal_load(calls)
+ initialize(calls)
+ end
+
def method_missing(name, *args)
@calls << [name, args]
end
diff --git a/activesupport/lib/active_support/testing/mochaing.rb b/activesupport/lib/active_support/testing/mochaing.rb
index 4ad75a6681..ae4e7e9a1f 100644
--- a/activesupport/lib/active_support/testing/mochaing.rb
+++ b/activesupport/lib/active_support/testing/mochaing.rb
@@ -1,7 +1,7 @@
begin
- silence_warnings { require 'mocha' }
+ silence_warnings { require 'mocha/setup' }
rescue LoadError
# Fake Mocha::ExpectationError so we can rescue it in #run. Bleh.
Object.const_set :Mocha, Module.new
Mocha.const_set :ExpectationError, Class.new(StandardError)
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb
index 96718d4bd3..50c4852d4c 100644
--- a/activesupport/lib/active_support/testing/performance/ruby.rb
+++ b/activesupport/lib/active_support/testing/performance/ruby.rb
@@ -142,7 +142,7 @@ module ActiveSupport
end
end
-if RUBY_VERSION.between?('1.9.2', '2.0')
+if RUBY_VERSION.between?('1.9.2', '2.0.0')
require 'active_support/testing/performance/ruby/yarv'
elsif RUBY_VERSION.between?('1.8.6', '1.9')
require 'active_support/testing/performance/ruby/mri'
diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb
index f2268d1767..e5353f65cf 100644
--- a/activesupport/lib/active_support/testing/setup_and_teardown.rb
+++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb
@@ -61,7 +61,7 @@ module ActiveSupport
def run(result)
return if @method_name.to_s == "default_test"
- mocha_counter = retrieve_mocha_counter(result)
+ mocha_counter = retrieve_mocha_counter(self, result)
yield(Test::Unit::TestCase::STARTED, name)
@_result = result
@@ -83,6 +83,8 @@ module ActiveSupport
begin
teardown
run_callbacks :teardown
+ rescue Mocha::ExpectationError => e
+ add_failure(e.message, e.backtrace)
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue Exception => e
@@ -100,19 +102,20 @@ module ActiveSupport
protected
- def retrieve_mocha_counter(result) #:nodoc:
+ def retrieve_mocha_counter(test_case, result) #:nodoc:
if respond_to?(:mocha_verify) # using mocha
if defined?(Mocha::TestCaseAdapter::AssertionCounter)
Mocha::TestCaseAdapter::AssertionCounter.new(result)
elsif defined?(Mocha::Integration::TestUnit::AssertionCounter)
Mocha::Integration::TestUnit::AssertionCounter.new(result)
- else
+ elsif defined?(Mocha::MonkeyPatching::TestUnit::AssertionCounter)
Mocha::MonkeyPatching::TestUnit::AssertionCounter.new(result)
+ else
+ Mocha::Integration::AssertionCounter.new(test_case)
end
end
end
end
-
end
end
end
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index a3c378f057..f39b58a980 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -329,8 +329,7 @@ module ActiveSupport
# Send the missing method to +time+ instance, and wrap result in a new TimeWithZone with the existing +time_zone+.
def method_missing(sym, *args, &block)
- result = time.__send__(sym, *args, &block)
- result.acts_like?(:time) ? self.class.new(nil, time_zone, result) : result
+ wrap_with_time_zone time.__send__(sym, *args, &block)
end
private
@@ -348,11 +347,22 @@ module ActiveSupport
end
def transfer_time_values_to_utc_constructor(time)
- ::Time.utc_time(time.year, time.month, time.day, time.hour, time.min, time.sec, time.respond_to?(:usec) ? time.usec : 0)
+ usec = time.respond_to?(:nsec) ? Rational(time.nsec, 1000) : (time.respond_to?(:usec) ? time.usec : 0)
+ ::Time.utc_time(time.year, time.month, time.day, time.hour, time.min, time.sec, usec)
end
def duration_of_variable_length?(obj)
ActiveSupport::Duration === obj && obj.parts.any? {|p| p[0].in?([:years, :months, :days]) }
end
+
+ def wrap_with_time_zone(time)
+ if time.acts_like?(:time)
+ self.class.new(nil, time_zone, time)
+ elsif time.is_a?(Range)
+ wrap_with_time_zone(time.begin)..wrap_with_time_zone(time.end)
+ else
+ time
+ end
+ end
end
end
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index c7d8fc2e17..f935180036 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -268,13 +268,23 @@ module ActiveSupport
# Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
# Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
def parse(str, now=now)
- date_parts = Date._parse(str)
- return if date_parts.blank?
- time = Time.parse(str, now) rescue DateTime.parse(str)
- if date_parts[:offset].nil?
- ActiveSupport::TimeWithZone.new(nil, self, time)
+ parts = Date._parse(str, false)
+ return if parts.empty?
+
+ time = Time.utc(
+ parts.fetch(:year, now.year),
+ parts.fetch(:mon, now.month),
+ parts.fetch(:mday, now.day),
+ parts.fetch(:hour, 0),
+ parts.fetch(:min, 0),
+ parts.fetch(:sec, 0),
+ parts.fetch(:sec_fraction, 0) * 1000000
+ )
+
+ if parts[:offset]
+ TimeWithZone.new(time - parts[:offset], self)
else
- time.in_time_zone(self)
+ TimeWithZone.new(nil, self, time)
end
end
diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb
index e928403dd2..03b1e511d9 100644
--- a/activesupport/lib/active_support/version.rb
+++ b/activesupport/lib/active_support/version.rb
@@ -2,7 +2,7 @@ module ActiveSupport
module VERSION #:nodoc:
MAJOR = 3
MINOR = 2
- TINY = 11
+ TINY = 13
PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 6c222b83ba..8d23ce4e18 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -38,6 +38,12 @@ module ActiveSupport
{}
else
@dbf = DocumentBuilderFactory.new_instance
+ # secure processing of java xml
+ # http://www.ibm.com/developerworks/xml/library/x-tipcfsx/index.html
+ @dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
+ @dbf.setFeature("http://xml.org/sax/features/external-general-entities", false)
+ @dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false)
+ @dbf.setFeature(javax.xml.XMLConstants::FEATURE_SECURE_PROCESSING, true)
xml_string_reader = StringReader.new(data)
xml_input_source = InputSource.new(xml_string_reader)
doc = @dbf.new_document_builder.parse(xml_input_source)
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index 0382739871..c26a48a490 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -25,7 +25,7 @@ end
require 'test/unit'
require 'empty_bool'
-silence_warnings { require 'mocha' }
+silence_warnings { require 'mocha/setup' }
ENV['NO_RELOAD'] = '1'
require 'active_support'
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index c4c753caed..6db1746672 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -690,7 +690,6 @@ uses_memcached 'memcached backed store' do
@data = @cache.instance_variable_get(:@data)
@cache.clear
@cache.silence!
- @cache.logger = Logger.new("/dev/null")
end
include CacheStoreBehavior
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index c040d86327..37bbcc7d39 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -350,14 +350,14 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
end
def test_end_of_day
- assert_equal Time.local(2005,2,21,23,59,59,999999.999), Date.new(2005,2,21).end_of_day
+ assert_equal Time.local(2005,2,21,23,59,59,Rational(999999999, 1000)), Date.new(2005,2,21).end_of_day
end
def test_end_of_day_when_zone_is_set
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
with_env_tz 'UTC' do
with_tz_default zone do
- assert_equal zone.local(2005,2,21,23,59,59,999999.999), Date.new(2005,2,21).end_of_day
+ assert_equal zone.local(2005,2,21,23,59,59,Rational(999999999, 1000)), Date.new(2005,2,21).end_of_day
assert_equal zone, Date.new(2005,2,21).end_of_day.time_zone
end
end
diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb
index 360c97031c..e9734652bc 100644
--- a/activesupport/test/core_ext/range_ext_test.rb
+++ b/activesupport/test/core_ext/range_ext_test.rb
@@ -12,6 +12,12 @@ class RangeTest < Test::Unit::TestCase
date_range = Time.utc(2005, 12, 10, 15, 30)..Time.utc(2005, 12, 10, 17, 30)
assert_equal "BETWEEN '2005-12-10 15:30:00' AND '2005-12-10 17:30:00'", date_range.to_s(:db)
end
+
+ def test_date_range
+ assert_instance_of Range, DateTime.new..DateTime.new
+ assert_instance_of Range, DateTime::Infinity.new..DateTime::Infinity.new
+ assert_instance_of Range, DateTime.new..DateTime::Infinity.new
+ end
def test_overlaps_last_inclusive
assert((1..5).overlaps?(5..10))
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index c030611de3..0d680832ef 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -109,49 +109,49 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_end_of_day
- assert_equal Time.local(2007,8,12,23,59,59,999999.999), Time.local(2007,8,12,10,10,10).end_of_day
+ assert_equal Time.local(2007,8,12,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,12,10,10,10).end_of_day
with_env_tz 'US/Eastern' do
- assert_equal Time.local(2007,4,2,23,59,59,999999.999), Time.local(2007,4,2,10,10,10).end_of_day, 'start DST'
- assert_equal Time.local(2007,10,29,23,59,59,999999.999), Time.local(2007,10,29,10,10,10).end_of_day, 'ends DST'
+ assert_equal Time.local(2007,4,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,4,2,10,10,10).end_of_day, 'start DST'
+ assert_equal Time.local(2007,10,29,23,59,59,Rational(999999999, 1000)), Time.local(2007,10,29,10,10,10).end_of_day, 'ends DST'
end
with_env_tz 'NZ' do
- assert_equal Time.local(2006,3,19,23,59,59,999999.999), Time.local(2006,3,19,10,10,10).end_of_day, 'ends DST'
- assert_equal Time.local(2006,10,1,23,59,59,999999.999), Time.local(2006,10,1,10,10,10).end_of_day, 'start DST'
+ assert_equal Time.local(2006,3,19,23,59,59,Rational(999999999, 1000)), Time.local(2006,3,19,10,10,10).end_of_day, 'ends DST'
+ assert_equal Time.local(2006,10,1,23,59,59,Rational(999999999, 1000)), Time.local(2006,10,1,10,10,10).end_of_day, 'start DST'
end
end
def test_end_of_week
- assert_equal Time.local(2008,1,6,23,59,59,999999.999), Time.local(2007,12,31,10,10,10).end_of_week
- assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,27,0,0,0).end_of_week #monday
- assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,28,0,0,0).end_of_week #tuesday
- assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,29,0,0,0).end_of_week #wednesday
- assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,30,0,0,0).end_of_week #thursday
- assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,31,0,0,0).end_of_week #friday
- assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,9,01,0,0,0).end_of_week #saturday
- assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,9,02,0,0,0).end_of_week #sunday
+ assert_equal Time.local(2008,1,6,23,59,59,Rational(999999999, 1000)), Time.local(2007,12,31,10,10,10).end_of_week
+ assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,27,0,0,0).end_of_week #monday
+ assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,28,0,0,0).end_of_week #tuesday
+ assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,29,0,0,0).end_of_week #wednesday
+ assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,30,0,0,0).end_of_week #thursday
+ assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,31,0,0,0).end_of_week #friday
+ assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,9,01,0,0,0).end_of_week #saturday
+ assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), 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
+ assert_equal Time.local(2005,2,4,19,59,59,Rational(999999999, 1000)), 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
- assert_equal Time.local(2005,4,30,23,59,59,999999.999), Time.local(2005,4,20,10,10,10).end_of_month
+ assert_equal Time.local(2005,3,31,23,59,59,Rational(999999999, 1000)), Time.local(2005,3,20,10,10,10).end_of_month
+ assert_equal Time.local(2005,2,28,23,59,59,Rational(999999999, 1000)), Time.local(2005,2,20,10,10,10).end_of_month
+ assert_equal Time.local(2005,4,30,23,59,59,Rational(999999999, 1000)), Time.local(2005,4,20,10,10,10).end_of_month
end
def test_end_of_quarter
- assert_equal Time.local(2007,3,31,23,59,59,999999.999), Time.local(2007,2,15,10,10,10).end_of_quarter
- assert_equal Time.local(2007,3,31,23,59,59,999999.999), Time.local(2007,3,31,0,0,0).end_of_quarter
- assert_equal Time.local(2007,12,31,23,59,59,999999.999), Time.local(2007,12,21,10,10,10).end_of_quarter
- assert_equal Time.local(2007,6,30,23,59,59,999999.999), Time.local(2007,4,1,0,0,0).end_of_quarter
- assert_equal Time.local(2008,6,30,23,59,59,999999.999), Time.local(2008,5,31,0,0,0).end_of_quarter
+ assert_equal Time.local(2007,3,31,23,59,59,Rational(999999999, 1000)), Time.local(2007,2,15,10,10,10).end_of_quarter
+ assert_equal Time.local(2007,3,31,23,59,59,Rational(999999999, 1000)), Time.local(2007,3,31,0,0,0).end_of_quarter
+ assert_equal Time.local(2007,12,31,23,59,59,Rational(999999999, 1000)), Time.local(2007,12,21,10,10,10).end_of_quarter
+ assert_equal Time.local(2007,6,30,23,59,59,Rational(999999999, 1000)), Time.local(2007,4,1,0,0,0).end_of_quarter
+ assert_equal Time.local(2008,6,30,23,59,59,Rational(999999999, 1000)), Time.local(2008,5,31,0,0,0).end_of_quarter
end
def test_end_of_year
- assert_equal Time.local(2007,12,31,23,59,59,999999.999), Time.local(2007,2,22,10,10,10).end_of_year
- assert_equal Time.local(2007,12,31,23,59,59,999999.999), Time.local(2007,12,31,10,10,10).end_of_year
+ assert_equal Time.local(2007,12,31,23,59,59,Rational(999999999, 1000)), Time.local(2007,2,22,10,10,10).end_of_year
+ assert_equal Time.local(2007,12,31,23,59,59,Rational(999999999, 1000)), Time.local(2007,12,31,10,10,10).end_of_year
end
def test_beginning_of_year
@@ -538,14 +538,16 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_to_s
- time = Time.utc(2005, 2, 21, 17, 44, 30)
- assert_equal time.to_default_s, time.to_s
- assert_equal time.to_default_s, time.to_s(:doesnt_exist)
- assert_equal "2005-02-21 17:44:30", time.to_s(:db)
- assert_equal "21 Feb 17:44", time.to_s(:short)
- assert_equal "17:44", time.to_s(:time)
- assert_equal "February 21, 2005 17:44", time.to_s(:long)
- assert_equal "February 21st, 2005 17:44", time.to_s(:long_ordinal)
+ time = Time.utc(2005, 2, 21, 17, 44, 30.12345678901)
+ assert_equal time.to_default_s, time.to_s
+ assert_equal time.to_default_s, time.to_s(:doesnt_exist)
+ assert_equal "2005-02-21 17:44:30", time.to_s(:db)
+ assert_equal "21 Feb 17:44", time.to_s(:short)
+ assert_equal "17:44", time.to_s(:time)
+ assert_equal "20050221174430", time.to_s(:number)
+ assert_equal "20050221174430123456789", time.to_s(:nsec) if RUBY_VERSION >= '1.9'
+ assert_equal "February 21, 2005 17:44", time.to_s(:long)
+ assert_equal "February 21st, 2005 17:44", time.to_s(:long_ordinal)
with_env_tz "UTC" do
assert_equal "Mon, 21 Feb 2005 17:44:30 +0000", time.to_s(:rfc822)
end
@@ -805,24 +807,32 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_all_day
- assert_equal Time.local(2011,6,7,0,0,0)..Time.local(2011,6,7,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_day
+ assert_equal Time.local(2011,6,7,0,0,0)..Time.local(2011,6,7,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_day
+ end
+
+ def test_all_day_with_timezone
+ beginning_of_day = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Hawaii"], Time.local(2011,6,7,0,0,0))
+ end_of_day = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Hawaii"], Time.local(2011,6,7,23,59,59,Rational(999999999, 1000)))
+
+ assert_equal beginning_of_day, ActiveSupport::TimeWithZone.new(Time.local(2011,6,7,10,10,10), ActiveSupport::TimeZone["Hawaii"]).all_day.begin
+ assert_equal end_of_day, ActiveSupport::TimeWithZone.new(Time.local(2011,6,7,10,10,10), ActiveSupport::TimeZone["Hawaii"]).all_day.end
end
def test_all_week
- assert_equal Time.local(2011,6,6,0,0,0)..Time.local(2011,6,12,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_week
- assert_equal Time.local(2011,6,5,0,0,0)..Time.local(2011,6,11,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_week(:sunday)
+ assert_equal Time.local(2011,6,6,0,0,0)..Time.local(2011,6,12,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_week
+ assert_equal Time.local(2011,6,5,0,0,0)..Time.local(2011,6,11,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_week(:sunday)
end
def test_all_month
- assert_equal Time.local(2011,6,1,0,0,0)..Time.local(2011,6,30,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_month
+ assert_equal Time.local(2011,6,1,0,0,0)..Time.local(2011,6,30,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_month
end
def test_all_quarter
- assert_equal Time.local(2011,4,1,0,0,0)..Time.local(2011,6,30,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_quarter
+ assert_equal Time.local(2011,4,1,0,0,0)..Time.local(2011,6,30,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_quarter
end
def test_all_year
- assert_equal Time.local(2011,1,1,0,0,0)..Time.local(2011,12,31,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_year
+ assert_equal Time.local(2011,1,1,0,0,0)..Time.local(2011,12,31,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_year
end
protected
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index 84afee6c84..b80a68711c 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -80,6 +80,16 @@ class TimeWithZoneTest < Test::Unit::TestCase
ActiveSupport.use_standard_json_time_format = old
end
+ if RUBY_VERSION >= '1.9'
+ def test_nsec
+ local = Time.local(2011,6,7,23,59,59,Rational(999999999, 1000))
+ with_zone = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Hawaii"], local)
+
+ assert_equal local.nsec, with_zone.nsec
+ assert_equal with_zone.nsec, 999999999
+ end
+ end
+
def test_strftime
assert_equal '1999-12-31 19:00:00 EST -0500', @twz.strftime('%Y-%m-%d %H:%M:%S %Z %z')
end
diff --git a/activesupport/test/fixtures/xml/jdom_doctype.dtd b/activesupport/test/fixtures/xml/jdom_doctype.dtd
new file mode 100644
index 0000000000..89480496ef
--- /dev/null
+++ b/activesupport/test/fixtures/xml/jdom_doctype.dtd
@@ -0,0 +1 @@
+<!ENTITY a "external entity">
diff --git a/activesupport/test/fixtures/xml/jdom_entities.txt b/activesupport/test/fixtures/xml/jdom_entities.txt
new file mode 100644
index 0000000000..0337fdaa08
--- /dev/null
+++ b/activesupport/test/fixtures/xml/jdom_entities.txt
@@ -0,0 +1 @@
+<!ENTITY a "hello">
diff --git a/activesupport/test/fixtures/xml/jdom_include.txt b/activesupport/test/fixtures/xml/jdom_include.txt
new file mode 100644
index 0000000000..239ca3afaf
--- /dev/null
+++ b/activesupport/test/fixtures/xml/jdom_include.txt
@@ -0,0 +1 @@
+include me
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index a9590f164f..e4e13c3f25 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -22,6 +22,15 @@ class TestJSONEncoding < Test::Unit::TestCase
end
end
+ class CustomWithOptions
+ attr_accessor :foo, :bar
+
+ def as_json(options={})
+ options[:only] = %w(foo bar)
+ super(options)
+ end
+ end
+
TrueTests = [[ true, %(true) ]]
FalseTests = [[ false, %(false) ]]
NilTests = [[ nil, %(null) ]]
@@ -91,11 +100,11 @@ class TestJSONEncoding < Test::Unit::TestCase
def test_utf8_string_encoded_properly_when_kcode_is_utf8
with_kcode 'UTF8' do
result = ActiveSupport::JSON.encode('€2.99')
- assert_equal '"\\u20ac2.99"', result
+ assert_equal '"€2.99"', result
assert_equal(Encoding::UTF_8, result.encoding) if result.respond_to?(:encoding)
result = ActiveSupport::JSON.encode('✎☺')
- assert_equal '"\\u270e\\u263a"', result
+ assert_equal '"✎☺"', result
assert_equal(Encoding::UTF_8, result.encoding) if result.respond_to?(:encoding)
end
end
@@ -104,11 +113,24 @@ class TestJSONEncoding < Test::Unit::TestCase
def test_non_utf8_string_transcodes
s = '二'.encode('Shift_JIS')
result = ActiveSupport::JSON.encode(s)
- assert_equal '"\\u4e8c"', result
+ assert_equal '"二"', result
assert_equal Encoding::UTF_8, result.encoding
end
end
+ def test_wide_utf8_chars
+ w = '𠜎'
+ result = ActiveSupport::JSON.encode(w)
+ assert_equal '"𠜎"', result
+ end
+
+ def test_wide_utf8_roundtrip
+ hash = { :string => "𐒑" }
+ json = ActiveSupport::JSON.encode(hash)
+ decoded_hash = ActiveSupport::JSON.decode(json)
+ assert_equal "𐒑", decoded_hash['string']
+ end
+
def test_exception_raised_when_encoding_circular_reference_in_array
a = [1]
a << a
@@ -239,6 +261,16 @@ class TestJSONEncoding < Test::Unit::TestCase
assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json)
end
+ def test_to_json_should_not_keep_options_around
+ f = CustomWithOptions.new
+ f.foo = "hello"
+ f.bar = "world"
+
+ hash = {"foo" => f, "other_hash" => {"foo" => "other_foo", "test" => "other_test"}}
+ assert_equal({ "foo" => { "foo" => "hello", "bar" => "world" }, "other_hash" => { "foo" => "other_foo", "test" => "other_test"} },
+ JSON.parse(hash.to_json))
+ end
+
def test_struct_encoding
Struct.new('UserNameAndEmail', :name, :email)
Struct.new('UserNameAndDate', :name, :date)
diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb
index 0b5f912dc4..a7fd9402c8 100644
--- a/activesupport/test/ordered_hash_test.rb
+++ b/activesupport/test/ordered_hash_test.rb
@@ -221,7 +221,6 @@ class OrderedHashTest < Test::Unit::TestCase
alternate = ActiveSupport::OrderedHash[ [
[1, 2],
[3, 4],
- "bad key value pair",
[ 'missing value' ]
]]
diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb
index c4653b1ae6..ab74d579fc 100644
--- a/activesupport/test/test_case_test.rb
+++ b/activesupport/test/test_case_test.rb
@@ -16,6 +16,9 @@ module ActiveSupport
def options
nil
end
+
+ def record(*args)
+ end
end
if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 8ecfc1e47e..bd4bfca82c 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -198,6 +198,62 @@ class TimeZoneTest < Test::Unit::TestCase
assert_equal Time.utc(1999,12,31,19), twz.time
end
+ def test_parse_should_not_black_out_system_timezone_dst_jump
+ with_env_tz('EET') do
+ zone = ActiveSupport::TimeZone['Pacific Time (US & Canada)']
+ twz = zone.parse('2012-03-25 03:29:00')
+ assert_equal [0, 29, 3, 25, 3, 2012], twz.to_a[0,6]
+ end
+ end
+
+ def test_parse_should_black_out_app_timezone_dst_jump
+ with_env_tz('EET') do
+ zone = ActiveSupport::TimeZone['Pacific Time (US & Canada)']
+ twz = zone.parse('2012-03-11 02:29:00')
+ assert_equal [0, 29, 3, 11, 3, 2012], twz.to_a[0,6]
+ end
+ end
+
+ def test_parse_with_javascript_date
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.parse("Mon May 28 2012 00:00:00 GMT-0700 (PDT)")
+ assert_equal Time.utc(2012, 5, 28, 7, 0, 0), twz.utc
+ end
+
+ def test_parse_with_missing_time_components
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ zone.stubs(:now).returns zone.local(1999, 12, 31, 12, 59, 59)
+ twz = zone.parse('2012-12-01')
+ assert_equal Time.utc(2012, 12, 1), twz.time
+ end
+
+ def test_parse_doesnt_use_local_dst
+ with_env_tz 'US/Eastern' do
+ zone = ActiveSupport::TimeZone['UTC']
+ twz = zone.parse('2013-03-10 02:00:00')
+ assert_equal Time.utc(2013, 3, 10, 2, 0, 0), twz.time
+ end
+ end
+
+ def test_parse_handles_dst_jump
+ with_env_tz 'US/Eastern' do
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.parse('2013-03-10 02:00:00')
+ assert_equal Time.utc(2013, 3, 10, 3, 0, 0), twz.time
+ end
+ end
+
+ def test_parse_with_fractional_seconds
+ zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = zone.parse('2013-03-13 00:00:00.000001')
+ assert_equal 1, twz.usec
+
+ if twz.respond_to?(:nsec)
+ twz = zone.parse('2013-03-13 00:00:00.000000001')
+ assert_equal 1, twz.nsec
+ end
+ end
+
def test_utc_offset_lazy_loaded_from_tzinfo_when_not_passed_in_to_initialize
tzinfo = TZInfo::Timezone.get('America/New_York')
zone = ActiveSupport::TimeZone.create(tzinfo.name, nil, tzinfo)
diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb
index 7f809e7898..dfe2c417ca 100644
--- a/activesupport/test/xml_mini/jdom_engine_test.rb
+++ b/activesupport/test/xml_mini/jdom_engine_test.rb
@@ -3,9 +3,11 @@ if RUBY_PLATFORM =~ /java/
require 'active_support/xml_mini'
require 'active_support/core_ext/hash/conversions'
- class JDOMEngineTest < Test::Unit::TestCase
+ class JDOMEngineTest < ActiveSupport::TestCase
include ActiveSupport
+ FILES_DIR = File.dirname(__FILE__) + '/../fixtures/xml'
+
def setup
@default_backend = XmlMini.backend
XmlMini.backend = 'JDOM'
@@ -30,10 +32,41 @@ if RUBY_PLATFORM =~ /java/
assert_equal 'image/png', file.content_type
end
+ def test_not_allowed_to_expand_entities_to_files
+ attack_xml = <<-EOT
+ <!DOCTYPE member [
+ <!ENTITY a SYSTEM "file://#{FILES_DIR}/jdom_include.txt">
+ ]>
+ <member>x&a;</member>
+ EOT
+ assert_equal 'x', Hash.from_xml(attack_xml)["member"]
+ end
+
+ def test_not_allowed_to_expand_parameter_entities_to_files
+ attack_xml = <<-EOT
+ <!DOCTYPE member [
+ <!ENTITY % b SYSTEM "file://#{FILES_DIR}/jdom_entities.txt">
+ %b;
+ ]>
+ <member>x&a;</member>
+ EOT
+ assert_raise Java::OrgXmlSax::SAXParseException do
+ assert_equal 'x', Hash.from_xml(attack_xml)["member"]
+ end
+ end
+
+
+ def test_not_allowed_to_load_external_doctypes
+ attack_xml = <<-EOT
+ <!DOCTYPE member SYSTEM "file://#{FILES_DIR}/jdom_doctype.dtd">
+ <member>x&a;</member>
+ EOT
+ assert_equal 'x', Hash.from_xml(attack_xml)["member"]
+ end
+
def test_exception_thrown_on_expansion_attack
- assert_raise NativeException do
+ assert_raise Java::OrgXmlSax::SAXParseException do
attack_xml = <<-EOT
- <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE member [
<!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
<!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index adca0839d9..c0ab4b8317 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,9 +1,50 @@
-## Rails 3.2.10 ##
+## unreleased ##
+
+* No changes.
+
+
+## Rails 3.2.13 (Mar 18, 2013) ##
+
+* No changes.
+
+
+## Rails 3.2.12 (Feb 11, 2013) ##
+
+* No changes.
+
+
+## Rails 3.2.11 (Jan 8, 2013) ##
+
+* No changes.
+
+
+## Rails 3.2.10 (Jan 2, 2013) ##
+
+* No changes.
+
## Rails 3.2.9 (Nov 12, 2012) ##
+* Quote column names in generates fixture files. This prevents
+ conflicts with reserved YAML keywords such as 'yes' and 'no'
+ Fix #8612.
+ Backport #8616.
+
+ *Yves Senn*
+
+* Engines with a dummy app include the rake tasks of dependencies in the app namespace. [Backport: #8262]
+ Fix #8229
+
+ *Yves Senn*
+
+* Add dummy app Rake tasks when --skip-test-unit and --dummy-path is passed to the plugin generator. [Backport #8139]
+ Fix #8121
+
+ *Yves Senn*
+
* Update supported ruby versions error message in ruby_version_check.rb *Lihan Li*
+
## Rails 3.2.8 (Aug 9, 2012) ##
* ERB scaffold generator use the `:data => { :confirm => "Text" }` syntax instead of `:confirm`.
diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile
index 26c95be031..73f4ce3695 100644
--- a/railties/guides/source/action_mailer_basics.textile
+++ b/railties/guides/source/action_mailer_basics.textile
@@ -480,7 +480,7 @@ As Action Mailer now uses the Mail gem, this becomes as simple as adding to your
<ruby>
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
- :address => "smtp.gmail.com",
+ :address => 'smtp.gmail.com',
:port => 587,
:domain => 'baci.lindsaar.net',
:user_name => '<username>',
diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile
index 15d24f9ac1..d2a445b508 100644
--- a/railties/guides/source/active_record_validations_callbacks.textile
+++ b/railties/guides/source/active_record_validations_callbacks.textile
@@ -901,8 +901,12 @@ Below is a simple example where we change the Rails behavior to always display t
<ruby>
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
- errors = Array(instance.error_message).join(',')
- %(#{html_tag}<span class="validation-error">&nbsp;#{errors}</span>).html_safe
+ if html_tag =~ /\<label/
+ html_tag
+ else
+ errors = Array(instance.error_message).join(',')
+ %(#{html_tag}<span class="validation-error">&nbsp;#{errors}</span>).html_safe
+ end
end
</ruby>
diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index fbccff5005..83e35214a5 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -3615,7 +3615,9 @@ File.atomic_write(joined_asset_path) do |cache|
end
</ruby>
-To accomplish this +atomic_write+ creates a temporary file. That's the file the code in the block actually writes to. On completion, the temporary file is renamed, which is an atomic operation on POSIX systems. If the target file exists +atomic_write+ overwrites it and keeps owners and permissions.
+To accomplish this `atomic_write` creates a temporary file. That's the file the code in the block actually writes to. On completion, the temporary file is renamed, which is an atomic operation on POSIX systems. If the target file exists `atomic_write` overwrites it and keeps owners and permissions. However there are a few cases where `atomic_write` cannot change the file ownership or permissions, this error is caught and skipped over trusting in the user/filesystem to ensure the file is accessible to the processes that need it.
+
+NOTE. Due to the chmod operation `atomic_write` performs, if the target file has an ACL set on it this ACL will be recalculated/modified.
WARNING. Note you can't append with +atomic_write+.
diff --git a/railties/guides/source/ajax_on_rails.textile b/railties/guides/source/ajax_on_rails.textile
deleted file mode 100644
index 3a0ccfe9b2..0000000000
--- a/railties/guides/source/ajax_on_rails.textile
+++ /dev/null
@@ -1,267 +0,0 @@
-h2. AJAX on Rails
-
-This guide covers the built-in Ajax/JavaScript functionality of Rails (and more); it will enable you to create rich and dynamic AJAX applications with ease! We will cover the following topics:
-
-* Quick introduction to AJAX and related technologies
-* Unobtrusive JavaScript helpers with drivers for Prototype, jQuery etc
-* Testing JavaScript functionality
-
-endprologue.
-
-h3. Hello AJAX - a Quick Intro
-
-You'll need the basics of DOM, HTTP requests and other topics discussed here to really understand Ajax on Rails.
-
-h4. Asynchronous JavaScript + XML
-
-Basic terminology, new style of creating web apps
-
-h4. The DOM
-
-basics of the DOM, how is it built, properties, features, why is it central to AJAX
-
-h4. Standard HTML communication vs AJAX
-
-How do 'standard' and AJAX requests differ, why does this matter for understanding AJAX on Rails (tie in for *_remote helpers, the next section)
-
-h3. Built-in Rails Helpers
-
-Rails 3.1 ships with "jQuery":http://jquery.com as the default JavaScript library. The Gemfile contains <tt>gem 'jquery-rails'</tt> which makes the jQuery files available to the application automatically. This can be accessed as:
-
-<ruby>
-javascript_include_tag :defaults
-</ruby>
-
-h4. Examples
-
-All the remote_method helpers has been removed. To make them working with AJAX, simply pass the <tt>:remote => true</tt> option to the original non-remote method.
-
-<ruby>
-button_to "New", :action => "new", :form_class => "new-thing"
-</ruby>
-
-will produce
-
-<html>
-<form method="post" action="/controller/new" class="new-thing">
- <div><input value="New" type="submit" /></div>
-</form>
-</html>
-
-<ruby>
-button_to "Create", :action => "create", :remote => true, :form => { "data-type" => "json" }
-</ruby>
-
-will produce
-
-<html>
-<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
- <div><input value="Create" type="submit" /></div>
-</form>
-</html>
-
-<ruby>
-button_to "Delete Image", { :action => "delete", :id => @image.id },
- :confirm => "Are you sure?", :method => :delete
-</ruby>
-
-will produce
-
-<html>
-<form method="post" action="/images/delete/1" class="button_to">
- <div>
- <input type="hidden" name="_method" value="delete" />
- <input data-confirm='Are you sure?' value="Delete" type="submit" />
- </div>
-</form>
-</html>
-
-<ruby>
-button_to('Destroy', 'http://www.example.com', :confirm => 'Are you sure?',
- :method => "delete", :remote => true, :disable_with => 'loading...')
-</ruby>
-
-will produce
-
-<html>
-<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?' />
- </div>
-</form>
-</html>
-
-You can also choose to use Prototype instead of jQuery and specify the option using +-j+ switch while generating the application.
-
-<shell>
-rails new app_name -j prototype
-</shell>
-
-You are ready to add some AJAX love to your Rails app!
-
-h4. The Quintessential AJAX Rails Helper: link_to_remote
-
-Let's start with what is probably the most often used helper: +link_to_remote+. It has an interesting feature from the documentation point of view: the options supplied to +link_to_remote+ are shared by all other AJAX helpers, so learning the mechanics and options of +link_to_remote+ is a great help when using other helpers.
-
-The signature of +link_to_remote+ function is the same as that of the standard +link_to+ helper:
-
-<ruby>
-def link_to_remote(name, options = {}, html_options = nil)
-</ruby>
-
-And here is a simple example of link_to_remote in action:
-
-<ruby>
-link_to_remote "Add to cart",
- :url => add_to_cart_url(product.id),
- :update => "cart"
-</ruby>
-
-* The very first parameter, a string, is the text of the link which appears on the page.
-* The second parameter, the +options+ hash is the most interesting part as it has the AJAX specific stuff:
-** *:url* This is the only parameter that is always required to generate the simplest remote link (technically speaking, it is not required, you can pass an empty +options+ hash to +link_to_remote+ - but in this case the URL used for the POST request will be equal to your current URL which is probably not your intention). This URL points to your AJAX action handler. The URL is typically specified by Rails REST view helpers, but you can use the +url_for+ format too.
-** *:update* Specifying a DOM id of the element we would like to update. The above example demonstrates the simplest way of accomplishing this - however, we are in trouble if the server responds with an error message because that will be injected into the page too! However, Rails has a solution for this situation:
-
-<ruby>
-link_to_remote "Add to cart",
- :url => add_to_cart_url(product),
- :update => { :success => "cart", :failure => "error" }
-</ruby>
-
-If the server returns 200, the output of the above example is equivalent to our first, simple one. However, in case of error, the element with the DOM id +error+ is updated rather than the +cart+ element.
-
-** *position* By default (i.e. when not specifying this option, like in the examples before) the response is injected into the element with the specified DOM id, replacing the original content of the element (if there was any). You might want to alter this behavior by keeping the original content - the only question is where to place the new content? This can specified by the +position+ parameter, with four possibilities:
-*** +:before+ Inserts the response text just before the target element. More precisely, it creates a text node from the response and inserts it as the left sibling of the target element.
-*** +:after+ Similar behavior to +:before+, but in this case the response is inserted after the target element.
-*** +:top+ Inserts the text into the target element, before it's original content. If the target element was empty, this is equivalent with not specifying +:position+ at all.
-*** +:bottom+ The counterpart of +:top+: the response is inserted after the target element's original content.
-
-A typical example of using +:bottom+ is inserting a new &lt;li&gt; element into an existing list:
-
-<ruby>
-link_to_remote "Add new item",
- :url => items_url,
- :update => 'item_list',
- :position => :bottom
-</ruby>
-
-** *:method* Most typically you want to use a POST request when adding a remote link to your view so this is the default behavior. However, sometimes you'll want to update (PUT) or delete/destroy (DELETE) something and you can specify this with the +:method+ option. Let's see an example for a typical AJAX link for deleting an item from a list:
-
-<ruby>
-link_to_remote "Delete the item",
- :url => item_url(item),
- :method => :delete
-</ruby>
-
-Note that if we wouldn't override the default behavior (POST), the above snippet would route to the create action rather than destroy.
-
-** *JavaScript filters* You can customize the remote call further by wrapping it with some JavaScript code. Let's say in the previous example, when deleting a link, you'd like to ask for a confirmation by showing a simple modal text box to the user. This is a typical example what you can accomplish with these options - let's see them one by one:
-*** +:confirm+ =&gt; +msg+ Pops up a JavaScript confirmation dialog, displaying +msg+. If the user chooses 'OK', the request is launched, otherwise canceled.
-*** +:condition+ =&gt; +code+ Evaluates +code+ (which should evaluate to a boolean) and proceeds if it's true, cancels the request otherwise.
-*** +:before+ =&gt; +code+ Evaluates the +code+ just before launching the request. The output of the code has no influence on the execution. Typically used show a progress indicator (see this in action in the next example).
-*** +:after+ =&gt; +code+ Evaluates the +code+ after launching the request. Note that this is different from the +:success+ or +:complete+ callback (covered in the next section) since those are triggered after the request is completed, while the code snippet passed to +:after+ is evaluated after the remote call is made. A common example is to disable elements on the page or otherwise prevent further action while the request is completed.
-*** +:submit+ =&gt; +dom_id+ This option does not make sense for +link_to_remote+, but we'll cover it for the sake of completeness. By default, the parent element of the form elements the user is going to submit is the current form - use this option if you want to change the default behavior. By specifying this option you can change the parent element to the element specified by the DOM id +dom_id+.
-*** +:with+ &gt; +code+ The JavaScript code snippet in +code+ is evaluated and added to the request URL as a parameter (or set of parameters). Therefore, +code+ should return a valid URL query string (like "item_type=8" or "item_type=8&sort=true"). Usually you want to obtain some value(s) from the page - let's see an example:
-
-<ruby>
-link_to_remote "Update record",
- :url => record_url(record),
- :method => :put,
- :with => "'status=' <plus> 'encodeURIComponent($('status').value) <plus> '&completed=' <plus> $('completed')"
-</ruby>
-
-This generates a remote link which adds 2 parameters to the standard URL generated by Rails, taken from the page (contained in the elements matched by the 'status' and 'completed' DOM id).
-
-** *Callbacks* Since an AJAX call is typically asynchronous, as it's name suggests (this is not a rule, and you can fire a synchronous request - see the last option, +:type+) your only way of communicating with a request once it is fired is via specifying callbacks. There are six options at your disposal (in fact 508, counting all possible response types, but these six are the most frequent and therefore specified by a constant):
-*** +:loading:+ =&gt; +code+ The request is in the process of receiving the data, but the transfer is not completed yet.
-*** +:loaded:+ =&gt; +code+ The transfer is completed, but the data is not processed and returned yet
-*** +:interactive:+ =&gt; +code+ One step after +:loaded+: The data is fully received and being processed
-*** +:success:+ =&gt; +code+ The data is fully received, parsed and the server responded with "200 OK"
-*** +:failure:+ =&gt; +code+ The data is fully received, parsed and the server responded with *anything* but "200 OK" (typically 404 or 500, but in general with any status code ranging from 100 to 509)
-*** +:complete:+ =&gt; +code+ The combination of the previous two: The request has finished receiving and parsing the data, and returned a status code (which can be anything).
-*** Any other status code ranging from 100 to 509: Additionally you might want to check for other HTTP status codes, such as 404. In this case simply use the status code as a number:
-<ruby>
-link_to_remote "Add new item",
- :url => items_url,
- :update => "item_list",
- 404 => "alert('Item not found!')"
-</ruby>
-Let's see a typical example for the most frequent callbacks, +:success+, +:failure+ and +:complete+ in action:
-
-<ruby>
-link_to_remote "Add new item",
- :url => items_url,
- :update => "item_list",
- :before => "$('progress').show()",
- :complete => "$('progress').hide()",
- :success => "display_item_added(request)",
- :failure => "display_error(request)"
-</ruby>
-
-** *:type* If you want to fire a synchronous request for some obscure reason (blocking the browser while the request is processed and doesn't return a status code), you can use the +:type+ option with the value of +:synchronous+.
-* Finally, using the +html_options+ parameter you can add HTML attributes to the generated tag. It works like the same parameter of the +link_to+ helper. There are interesting side effects for the +href+ and +onclick+ parameters though:
-** If you specify the +href+ parameter, the AJAX link will degrade gracefully, i.e. the link will point to the URL even if JavaScript is disabled in the client browser
-** +link_to_remote+ gains it's AJAX behavior by specifying the remote call in the onclick handler of the link. If you supply +html_options[:onclick]+ you override the default behavior, so use this with care!
-
-We are finished with +link_to_remote+. I know this is quite a lot to digest for one helper function, but remember, these options are common for all the rest of the Rails view helpers, so we will take a look at the differences / additional parameters in the next sections.
-
-h4. AJAX Forms
-
-There are three different ways of adding AJAX forms to your view using Rails Prototype helpers. They are slightly different, but striving for the same goal: instead of submitting the form using the standard HTTP request/response cycle, it is submitted asynchronously, thus not reloading the page. These methods are the following:
-
-* +remote_form_for+ (and it's alias +form_remote_for+) is tied to Rails most tightly of the three since it takes a resource, model or array of resources (in case of a nested resource) as a parameter.
-* +form_remote_tag+ AJAXifies the form by serializing and sending it's data in the background
-* +submit_to_remote+ and +button_to_remote+ is more rarely used than the previous two. Rather than creating an AJAX form, you add a button/input
-
-Let's see them in action one by one!
-
-h5. +remote_form_for+
-
-h5. +form_remote_tag+
-
-h5. +submit_to_remote+
-
-h4. Observing Elements
-
-h5. +observe_field+
-
-h5. +observe_form+
-
-h4. Calling a Function Periodically
-
-h5. +periodically_call_remote+
-
-
-h4. Miscellaneous Functionality
-
-h5. +remote_function+
-
-h5. +update_page+
-
-h4. Serving JavaScript
-
-First we'll check out how to send JavaScript to the server manually. You are practically never going to need this, but it's interesting to understand what's going on under the hood.
-
-<ruby>
-def javascript_test
- render :text => "alert('Hello, world!')",
- :content_type => "text/javascript"
-end
-</ruby>
-
-(Note: if you want to test the above method, create a +link_to_remote+ with a single parameter - +:url+, pointing to the +javascript_test+ action)
-
-What happens here is that by specifying the Content-Type header variable, we instruct the browser to evaluate the text we are sending over (rather than displaying it as plain text, which is the default behavior).
-
-h3. Testing JavaScript
-
-JavaScript testing reminds me the definition of the world 'classic' by Mark Twain: "A classic is something that everybody wants to have read and nobody wants to read." It's similar with JavaScript testing: everyone would like to have it, yet it's not done by too much developers as it is tedious, complicated, there is a proliferation of tools and no consensus/accepted best practices, but we will nevertheless take a stab at it:
-
-* (Fire)Watir
-* Selenium
-* Celerity/Culerity
-* Cucumber+Webrat
-* Mention stuff like screw.unit/jsSpec
-
-Note to self: check out the RailsConf JS testing video
diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile
index 0e811a2527..c2ca5a335d 100644
--- a/railties/guides/source/caching_with_rails.textile
+++ b/railties/guides/source/caching_with_rails.textile
@@ -86,9 +86,9 @@ Or, you can set custom gzip compression level (level names are taken from +Zlib+
caches_page :image, :gzip => :best_speed
</ruby>
-NOTE: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. A workaround for this limitation is to include the parameters in the page's path, e.g. +/productions/page/1+.
+NOTE: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. A workaround for this limitation is to include the parameters in the products's path, e.g. +/products/page/1+.
-INFO: Page caching runs in an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job.
+INFO: Page caching runs as an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job.
h4. Action Caching
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index 3466e1c3f4..f5b39dd4ac 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -277,6 +277,8 @@ h4. Configuring Active Record
* +config.active_record.auto_explain_threshold_in_seconds+ configures the threshold for automatic EXPLAINs (+nil+ disables this feature). Queries exceeding the threshold get their query plan logged. Default is 0.5 in development mode.
+* +config.active_record.cache_timestamp_format+ controls the format of the timestamp value in the cache key. Default is +:number+.
+
The MySQL adapter adds one additional configuration option:
* +ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans+ controls whether Active Record will consider all +tinyint(1)+ columns in a MySQL database to be booleans and is true by default.
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index c32a23c50b..99409edbd0 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -1153,6 +1153,7 @@ First, take a look at +comment.rb+:
<ruby>
class Comment < ActiveRecord::Base
+ attr_accesssible :body, :commenter, :post
belongs_to :post
end
</ruby>
@@ -1215,6 +1216,7 @@ makes each comment belong to a Post:
<ruby>
class Comment < ActiveRecord::Base
+ attr_accessible :body, :commenter, :post
belongs_to :post
end
</ruby>
diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile
index 958b13cd9e..0cf7d4a4fb 100644
--- a/railties/guides/source/performance_testing.textile
+++ b/railties/guides/source/performance_testing.textile
@@ -447,7 +447,7 @@ h4. Using Ruby-Prof on MRI and REE
Add Ruby-Prof to your applications' Gemfile if you want to benchmark/profile under MRI or REE:
<ruby>
-gem 'ruby-prof', :git => 'git://github.com/wycats/ruby-prof.git'
+gem 'ruby-prof'
</ruby>
Now run +bundle install+ and you're ready to go.
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 854ac2cbbc..4f695159ea 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -167,7 +167,7 @@ module Rails
# These parameters will be used by middlewares and engines to configure themselves.
#
def env_config
- @env_config ||= super.merge({
+ @app_env_config ||= super.merge({
"action_dispatch.parameter_filter" => config.filter_parameters,
"action_dispatch.secret_token" => config.secret_token,
"action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
diff --git a/railties/lib/rails/generators/rails/app/templates/README b/railties/lib/rails/generators/rails/app/templates/README
index 7c36f2356e..3e1c15c81d 100644
--- a/railties/lib/rails/generators/rails/app/templates/README
+++ b/railties/lib/rails/generators/rails/app/templates/README
@@ -157,9 +157,9 @@ The default directory structure of a generated Ruby on Rails application:
|-- app
| |-- assets
- | |-- images
- | |-- javascripts
- | `-- stylesheets
+ | | |-- images
+ | | |-- javascripts
+ | | `-- stylesheets
| |-- controllers
| |-- helpers
| |-- mailers
@@ -173,6 +173,7 @@ The default directory structure of a generated Ruby on Rails application:
|-- db
|-- doc
|-- lib
+ | |-- assets
| `-- tasks
|-- log
|-- public
@@ -184,13 +185,12 @@ The default directory structure of a generated Ruby on Rails application:
| |-- performance
| `-- unit
|-- tmp
- | |-- cache
- | |-- pids
- | |-- sessions
- | `-- sockets
+ | `-- cache
+ | `-- assets
`-- vendor
|-- assets
- `-- stylesheets
+ | |-- javascripts
+ | `-- stylesheets
`-- plugins
app
diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
index cd7d51e628..1b7f5dee2a 100644
--- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
@@ -204,7 +204,7 @@ task :default => :test
end
def create_test_dummy_files
- return if options[:skip_test_unit] && options[:dummy_path] == 'test/dummy'
+ return unless with_dummy_app?
create_dummy_app
end
@@ -214,6 +214,18 @@ task :default => :test
public_task :apply_rails_template, :run_bundle
+ def name
+ @name ||= begin
+ # same as ActiveSupport::Inflector#underscore except not replacing '-'
+ underscored = original_name.dup
+ underscored.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
+ underscored.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
+ underscored.downcase!
+
+ underscored
+ end
+ end
+
protected
def app_templates_dir
@@ -242,6 +254,10 @@ task :default => :test
options[:mountable]
end
+ def with_dummy_app?
+ options[:skip_test_unit].blank? || options[:dummy_path] != 'test/dummy'
+ end
+
def self.banner
"rails plugin new #{self.arguments.map(&:usage).join(' ')} [options]"
end
@@ -250,18 +266,6 @@ task :default => :test
@original_name ||= File.basename(destination_root)
end
- def name
- @name ||= begin
- # same as ActiveSupport::Inflector#underscore except not replacing '-'
- underscored = original_name.dup
- underscored.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
- underscored.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
- underscored.downcase!
-
- underscored
- end
- end
-
def camelized
@camelized ||= name.gsub(/\W/, '_').squeeze('_').camelize
end
@@ -282,7 +286,7 @@ task :default => :test
dummy_application_path = File.expand_path("#{dummy_path}/config/application.rb", destination_root)
unless options[:pretend] || !File.exists?(dummy_application_path)
contents = File.read(dummy_application_path)
- contents[(contents.index("module Dummy"))..-1]
+ contents[(contents.index(/module ([\w]+)\n(.*)class Application/m))..-1]
end
end
end
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile
index 564fda3c49..9b3b8cc03f 100755
--- a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile
@@ -20,7 +20,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_files.include('lib/**/*.rb')
end
-<% if full? && !options[:skip_active_record] && !options[:skip_test_unit] -%>
+<% if full? && !options[:skip_active_record] && with_dummy_app? -%>
APP_RAKEFILE = File.expand_path("../<%= dummy_path -%>/Rakefile", __FILE__)
load 'rails/tasks/engine.rake'
<% end %>
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb b/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb
index 996ea79e67..6cb7e38b56 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb
@@ -12,7 +12,7 @@ require "active_resource/railtie"
<%= comment_if :skip_test_unit %>require "rails/test_unit/railtie"
<% end -%>
-Bundler.require
+Bundler.require(*Rails.groups)
require "<%= name %>"
<%= application_definition %>
diff --git a/railties/lib/rails/generators/test_unit/model/model_generator.rb b/railties/lib/rails/generators/test_unit/model/model_generator.rb
index c1dd535dd3..9749a6b133 100644
--- a/railties/lib/rails/generators/test_unit/model/model_generator.rb
+++ b/railties/lib/rails/generators/test_unit/model/model_generator.rb
@@ -3,6 +3,9 @@ require 'rails/generators/test_unit'
module TestUnit
module Generators
class ModelGenerator < Base
+
+ RESERVED_YAML_KEYWORDS = %w(y yes n no true false on off null)
+
argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
class_option :fixture, :type => :boolean
@@ -19,6 +22,15 @@ module TestUnit
template 'fixtures.yml', File.join('test/fixtures', class_path, "#{plural_file_name}.yml")
end
end
+
+ private
+ def yaml_key_value(key, value)
+ if RESERVED_YAML_KEYWORDS.include?(key.downcase)
+ "'#{key}': #{value}"
+ else
+ "#{key}: #{value}"
+ end
+ end
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml
index 5c8780aa64..2c33766418 100644
--- a/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml
+++ b/railties/lib/rails/generators/test_unit/model/templates/fixtures.yml
@@ -3,12 +3,12 @@
<% unless attributes.empty? -%>
one:
<% attributes.each do |attribute| -%>
- <%= attribute.name %>: <%= attribute.default %>
+ <%= yaml_key_value(attribute.name, attribute.default) %>
<% end -%>
two:
<% attributes.each do |attribute| -%>
- <%= attribute.name %>: <%= attribute.default %>
+ <%= yaml_key_value(attribute.name, attribute.default) %>
<% end -%>
<% else -%>
# This model initially had no columns defined. If you add columns to the
diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb
index 352ecf45c0..0145879c87 100644
--- a/railties/lib/rails/version.rb
+++ b/railties/lib/rails/version.rb
@@ -2,7 +2,7 @@ module Rails
module VERSION #:nodoc:
MAJOR = 3
MINOR = 2
- TINY = 11
+ TINY = 13
PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index 9e9702efb6..adec9533a9 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -2,6 +2,7 @@
require 'isolation/abstract_unit'
require 'active_support/core_ext/kernel/reporting'
require 'rack/test'
+require 'yaml'
module ApplicationTests
class AssetsTest < Test::Unit::TestCase
diff --git a/railties/test/application/initializers/active_record_test.rb b/railties/test/application/initializers/active_record_test.rb
index edf78a8a0a..b62943a278 100644
--- a/railties/test/application/initializers/active_record_test.rb
+++ b/railties/test/application/initializers/active_record_test.rb
@@ -23,10 +23,17 @@ module ApplicationTests
boot_rails
simple_controller
- get '/foo'
- assert last_response.body.include?("We're sorry, but something went wrong (500)")
+ # ActiveSupport::LogSubscriber.flush_all! in lib/rails/rack/logger.rb blew up in Ruby 2.0
+ # because it tries to open the database. This behavior doesn't happen in Ruby 1.9.3.
+ # However, regardless, the server blew up.
+ if RUBY_VERSION >= '2.0.0'
+ assert_raises (Errno::ENOENT) { get '/foo' }
+ else
+ get '/foo'
+ assert last_response.body.include?("We're sorry, but something went wrong (500)")
+ end
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")
@@ -35,7 +42,7 @@ module ApplicationTests
get '/foo'
assert_equal 'foo', last_response.body
-
+
# clean up
FileUtils.rm("#{app_path}/#{database_path}")
end
diff --git a/railties/test/application/middleware/static_test.rb b/railties/test/application/middleware/static_test.rb
new file mode 100644
index 0000000000..82ee1b8360
--- /dev/null
+++ b/railties/test/application/middleware/static_test.rb
@@ -0,0 +1,31 @@
+# encoding: utf-8
+require 'isolation/abstract_unit'
+require 'rack/test'
+
+module ApplicationTests
+ class MiddlewareStaticTest < Test::Unit::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ # Regression test to #8907
+ # See https://github.com/rails/rails/commit/9cc82b77196d21a5c7021f6dca59ab9b2b158a45#commitcomment-2416514
+ test "doesn't set Cache-Control header when it is nil" do
+ app_file "public/foo.html", 'static'
+
+ require "#{app_path}/config/environment"
+
+ get 'foo'
+
+ assert !last_response.headers.has_key?('Cache-Control'), "Cache-Control should not be set"
+ end
+ end
+end
diff --git a/railties/test/application/rack/logger_test.rb b/railties/test/application/rack/logger_test.rb
index c13f9a49e7..984d8374fb 100644
--- a/railties/test/application/rack/logger_test.rb
+++ b/railties/test/application/rack/logger_test.rb
@@ -1,6 +1,7 @@
require "isolation/abstract_unit"
require "active_support/log_subscriber/test_helper"
require "rack/test"
+require "mocha/setup"
module ApplicationTests
module RackTests
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index ab9084df55..107b54c0be 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -122,6 +122,18 @@ module ApplicationTests
assert_equal 0, ::AppTemplate::Application::User.count
end
+ def test_loading_only_yml_fixtures
+ Dir.chdir(app_path) do
+ `rake db:migrate`
+ end
+
+ app_file "test/fixtures/products.csv", ""
+
+ require "#{rails_root}/config/environment"
+ errormsg = Dir.chdir(app_path) { `rake db:fixtures:load` }
+ assert $?.success?, errormsg
+ end
+
def test_scaffold_tests_pass_by_default
content = Dir.chdir(app_path) do
`rails generate scaffold user username:string password:string`
diff --git a/railties/test/application/route_inspect_test.rb b/railties/test/application/route_inspect_test.rb
index b897cf15b8..dea0ee71c9 100644
--- a/railties/test/application/route_inspect_test.rb
+++ b/railties/test/application/route_inspect_test.rb
@@ -1,5 +1,5 @@
require 'test/unit'
-require 'mocha'
+require 'mocha/setup'
require 'rails/application/route_inspector'
require 'action_controller'
require 'rails/engine'
@@ -18,7 +18,7 @@ module ApplicationTests
def test_displaying_routes_for_engines
engine = Class.new(Rails::Engine) do
- def self.to_s
+ def self.inspect
"Blog::Engine"
end
end
@@ -136,7 +136,7 @@ module ApplicationTests
def test_rake_routes_shows_route_with_rack_app_nested_with_dynamic_constraints
constraint = Class.new do
- def to_s
+ def inspect
"( my custom constraint )"
end
end
diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb
index a05e39658d..e50d744e26 100644
--- a/railties/test/application/routing_test.rb
+++ b/railties/test/application/routing_test.rb
@@ -229,6 +229,77 @@ module ApplicationTests
end
end
+ test 'routes are added and removed when reloading' do
+ app('development')
+
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render :text => "foo"
+ end
+ end
+ RUBY
+
+ controller :bar, <<-RUBY
+ class BarController < ApplicationController
+ def index
+ render :text => "bar"
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ AppTemplate::Application.routes.draw do
+ get 'foo', :to => 'foo#index'
+ end
+ RUBY
+
+ get '/foo'
+ assert_equal 'foo', last_response.body
+ assert_equal '/foo', Rails.application.routes.url_helpers.foo_path
+
+ get '/bar'
+ assert_equal 404, last_response.status
+ assert_raises NoMethodError do
+ assert_equal '/bar', Rails.application.routes.url_helpers.bar_path
+ end
+
+ app_file 'config/routes.rb', <<-RUBY
+ AppTemplate::Application.routes.draw do
+ get 'foo', :to => 'foo#index'
+ get 'bar', :to => 'bar#index'
+ end
+ RUBY
+
+ Rails.application.reload_routes!
+
+ get '/foo'
+ assert_equal 'foo', last_response.body
+ assert_equal '/foo', Rails.application.routes.url_helpers.foo_path
+
+ get '/bar'
+ assert_equal 'bar', last_response.body
+ assert_equal '/bar', Rails.application.routes.url_helpers.bar_path
+
+ app_file 'config/routes.rb', <<-RUBY
+ AppTemplate::Application.routes.draw do
+ get 'foo', :to => 'foo#index'
+ end
+ RUBY
+
+ Rails.application.reload_routes!
+
+ get '/foo'
+ assert_equal 'foo', last_response.body
+ assert_equal '/foo', Rails.application.routes.url_helpers.foo_path
+
+ get '/bar'
+ assert_equal 404, last_response.status
+ assert_raises NoMethodError do
+ assert_equal '/bar', Rails.application.routes.url_helpers.bar_path
+ end
+ end
+
test 'resource routing with irregular inflection' do
app_file 'config/initializers/inflection.rb', <<-RUBY
ActiveSupport::Inflector.inflections do |inflect|
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index f64abc1016..b96c591450 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -157,7 +157,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_match(/create_table :products/, up)
assert_match(/t\.string :name/, up)
assert_match(/t\.integer :supplier_id/, up)
-
+
assert_match(/add_index :products, :name/, up)
assert_match(/add_index :products, :supplier_id/, up)
assert_no_match(/add_index :products, :year/, up)
@@ -182,7 +182,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_match(/add_index :products, :discount, :unique => true/, content)
end
end
-
+
def test_migration_without_timestamps
ActiveRecord::Base.timestamped_migrations = false
run_generator ["account"]
@@ -261,7 +261,17 @@ class ModelGeneratorTest < Rails::Generators::TestCase
def test_invokes_default_test_framework
run_generator
assert_file "test/unit/account_test.rb", /class AccountTest < ActiveSupport::TestCase/
+
assert_file "test/fixtures/accounts.yml", /name: MyString/, /age: 1/
+ assert_generated_fixture("test/fixtures/accounts.yml",
+ {"one"=>{"name"=>"MyString", "age"=>1}, "two"=>{"name"=>"MyString", "age"=>1}})
+ end
+
+ def test_fixtures_respect_reserved_yml_keywords
+ run_generator ["LineItem", "no:integer", "Off:boolean", "ON:boolean"]
+
+ assert_generated_fixture("test/fixtures/line_items.yml",
+ {"one"=>{"no"=>1, "Off"=>false, "ON"=>false}, "two"=>{"no"=>1, "Off"=>false, "ON"=>false}})
end
def test_fixture_is_skipped
@@ -329,4 +339,11 @@ class ModelGeneratorTest < Rails::Generators::TestCase
run_generator ["Account"]
assert_file 'app/models/account.rb', /# attr_accessible :title, :body/
end
+
+ private
+ def assert_generated_fixture(path, parsed_contents)
+ fixture_file = File.new File.expand_path(path, destination_root)
+ assert_equal(parsed_contents, YAML.load(fixture_file))
+ end
+
end
diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb
index dcf4f95194..a33c6b096c 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -65,6 +65,12 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_no_match(/APP_RAKEFILE/, File.read(File.join(destination_root, "Rakefile")))
end
+ def test_generating_adds_dummy_app_rake_tasks_without_unit_test_files
+ run_generator [destination_root, "-T", "--mountable", '--dummy-path', 'my_dummy_app']
+
+ assert_match(/APP_RAKEFILE/, File.read(File.join(destination_root, "Rakefile")))
+ end
+
def test_ensure_that_plugin_options_are_not_passed_to_app_generator
FileUtils.cd(Rails.root)
assert_no_match(/It works from file!.*It works_from_file/, run_generator([destination_root, "-m", "lib/template.rb"]))
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 5f9ee220dc..01bd11e1ab 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -1,7 +1,7 @@
require 'generators/generators_test_helper'
require 'rails/generators/rails/model/model_generator'
require 'rails/generators/test_unit/model/model_generator'
-require 'mocha'
+require 'mocha/setup'
class GeneratorsTest < Rails::Generators::TestCase
include GeneratorsTestHelper
@@ -201,7 +201,7 @@ class GeneratorsTest < Rails::Generators::TestCase
mspec = Rails::Generators.find_by_namespace :fixjour
assert mspec.source_paths.include?(File.join(Rails.root, "lib", "templates", "fixjour"))
end
-
+
def test_usage_with_embedded_ruby
require File.expand_path("fixtures/lib/generators/usage_template/usage_template_generator", File.dirname(__FILE__))
output = capture(:stdout) { Rails::Generators.invoke :usage_template, ['--help'] }
diff --git a/version.rb b/version.rb
index 352ecf45c0..0145879c87 100644
--- a/version.rb
+++ b/version.rb
@@ -2,7 +2,7 @@ module Rails
module VERSION #:nodoc:
MAJOR = 3
MINOR = 2
- TINY = 11
+ TINY = 13
PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')