aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile10
-rw-r--r--RELEASING_RAILS.rdoc18
-rw-r--r--actionmailer/CHANGELOG.md16
-rw-r--r--actionmailer/lib/action_mailer.rb5
-rw-r--r--actionmailer/lib/action_mailer/base.rb2
-rw-r--r--actionmailer/lib/action_mailer/delivery_methods.rb4
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb1
-rw-r--r--actionmailer/test/base_test.rb14
-rw-r--r--actionmailer/test/delivery_methods_test.rb13
-rw-r--r--actionpack/CHANGELOG.md215
-rw-r--r--actionpack/README.rdoc285
-rw-r--r--actionpack/examples/performance.rb185
-rw-r--r--actionpack/examples/views/_collection.erb3
-rw-r--r--actionpack/examples/views/_hello.erb1
-rw-r--r--actionpack/examples/views/_hundred_partials.erb3
-rw-r--r--actionpack/examples/views/_partial.erb10
-rw-r--r--actionpack/examples/views/_ten_partials.erb10
-rw-r--r--actionpack/examples/views/hashes/_hash.erb3
-rw-r--r--actionpack/examples/views/my_hashes/_my_hash.erb3
-rw-r--r--actionpack/examples/views/template.html.erb1
-rw-r--r--actionpack/lib/action_controller.rb6
-rw-r--r--actionpack/lib/action_controller/base.rb9
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb44
-rw-r--r--actionpack/lib/action_controller/metal/data_streaming.rb4
-rw-r--r--actionpack/lib/action_controller/metal/force_ssl.rb2
-rw-r--r--actionpack/lib/action_controller/metal/live.rb31
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb4
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb10
-rw-r--r--actionpack/lib/action_controller/railtie.rb2
-rw-r--r--actionpack/lib/action_dispatch.rb9
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb21
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb5
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb11
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb9
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb6
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb9
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb67
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb38
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb2
-rw-r--r--actionpack/lib/action_view.rb6
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb84
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb15
-rw-r--r--actionpack/lib/action_view/helpers/cache_helper.rb32
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb26
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb26
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb16
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb3
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb40
-rw-r--r--actionpack/lib/action_view/helpers/sanitize_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb14
-rw-r--r--actionpack/lib/action_view/railtie.rb2
-rw-r--r--actionpack/lib/action_view/template/error.rb4
-rw-r--r--actionpack/test/abstract_unit.rb91
-rw-r--r--actionpack/test/activerecord/active_record_store_test.rb288
-rw-r--r--actionpack/test/controller/caching_test.rb42
-rw-r--r--actionpack/test/controller/mime_responds_test.rb9
-rw-r--r--actionpack/test/controller/resources_test.rb35
-rw-r--r--actionpack/test/controller/routing_test.rb22
-rw-r--r--actionpack/test/dispatch/cookies_test.rb2
-rw-r--r--actionpack/test/dispatch/live_response_test.rb17
-rw-r--r--actionpack/test/dispatch/mount_test.rb9
-rw-r--r--actionpack/test/dispatch/prefix_generation_test.rb18
-rw-r--r--actionpack/test/dispatch/response_test.rb29
-rw-r--r--actionpack/test/dispatch/routing/concerns_test.rb82
-rw-r--r--actionpack/test/dispatch/routing_test.rb2
-rw-r--r--actionpack/test/fixtures/test/_raise_indentation.html.erb13
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb9
-rw-r--r--actionpack/test/template/date_helper_test.rb53
-rw-r--r--actionpack/test/template/erb_util_test.rb2
-rw-r--r--actionpack/test/template/form_helper_test.rb28
-rw-r--r--actionpack/test/template/form_options_helper_test.rb24
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb16
-rw-r--r--actionpack/test/template/javascript_helper_test.rb42
-rw-r--r--actionpack/test/template/render_test.rb9
-rw-r--r--actionpack/test/template/sanitize_helper_test.rb4
-rw-r--r--actionpack/test/template/text_helper_test.rb2
-rw-r--r--actionpack/test/template/url_helper_test.rb2
-rw-r--r--activemodel/CHANGELOG.md31
-rw-r--r--activemodel/lib/active_model.rb16
-rw-r--r--activemodel/lib/active_model/callbacks.rb11
-rw-r--r--activemodel/lib/active_model/errors.rb8
-rw-r--r--activemodel/lib/active_model/naming.rb17
-rw-r--r--activemodel/lib/active_model/railtie.rb8
-rwxr-xr-x[-rw-r--r--]activemodel/lib/active_model/serializers/xml.rb4
-rw-r--r--activemodel/lib/active_model/validations/clusivity.rb13
-rw-r--r--activemodel/lib/active_model/validations/exclusion.rb3
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb3
-rw-r--r--activemodel/lib/active_model/validations/validates.rb7
-rw-r--r--activemodel/lib/active_model/validator.rb2
-rw-r--r--activemodel/test/cases/mass_assignment_security_test.rb10
-rwxr-xr-x[-rw-r--r--]activemodel/test/cases/serializers/xml_serialization_test.rb38
-rw-r--r--activemodel/test/cases/validations/exclusion_validation_test.rb22
-rw-r--r--activemodel/test/cases/validations/inclusion_validation_test.rb22
-rw-r--r--activemodel/test/cases/validations_test.rb9
-rw-r--r--activerecord/CHANGELOG.md153
-rw-r--r--activerecord/activerecord.gemspec2
-rw-r--r--activerecord/examples/performance.rb69
-rw-r--r--activerecord/lib/active_record.rb22
-rw-r--r--activerecord/lib/active_record/associations.rb31
-rw-r--r--activerecord/lib/active_record/associations/association.rb2
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb5
-rw-r--r--activerecord/lib/active_record/associations/builder/association.rb91
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb78
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb71
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb24
-rw-r--r--activerecord/lib/active_record/associations/builder/has_many.rb49
-rw-r--r--activerecord/lib/active_record/associations/builder/has_one.rb34
-rw-r--r--activerecord/lib/active_record/associations/builder/singular_association.rb22
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb16
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb22
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb67
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb212
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb3
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb123
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb4
-rw-r--r--activerecord/lib/active_record/core.rb19
-rw-r--r--activerecord/lib/active_record/counter_cache.rb2
-rw-r--r--activerecord/lib/active_record/dynamic_matchers.rb10
-rw-r--r--activerecord/lib/active_record/inheritance.rb2
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb18
-rw-r--r--activerecord/lib/active_record/model.rb2
-rw-r--r--activerecord/lib/active_record/model_schema.rb15
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb16
-rw-r--r--activerecord/lib/active_record/persistence.rb6
-rw-r--r--activerecord/lib/active_record/railtie.rb3
-rw-r--r--activerecord/lib/active_record/railties/databases.rake20
-rw-r--r--activerecord/lib/active_record/readonly_attributes.rb7
-rw-r--r--activerecord/lib/active_record/relation.rb5
-rw-r--r--activerecord/lib/active_record/relation/merger.rb10
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb23
-rw-r--r--activerecord/lib/active_record/result.rb4
-rw-r--r--activerecord/lib/active_record/session_store.rb365
-rw-r--r--activerecord/lib/active_record/store.rb2
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb2
-rw-r--r--activerecord/lib/active_record/transactions.rb22
-rw-r--r--activerecord/lib/rails/generators/active_record/session_migration/session_migration_generator.rb24
-rw-r--r--activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb12
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb7
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb7
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb19
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb4
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb41
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb61
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb6
-rw-r--r--activerecord/test/cases/attribute_methods/read_test.rb4
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb1
-rw-r--r--activerecord/test/cases/base_test.rb7
-rw-r--r--activerecord/test/cases/counter_cache_test.rb11
-rw-r--r--activerecord/test/cases/defaults_test.rb2
-rw-r--r--activerecord/test/cases/deprecated_dynamic_methods_test.rb2
-rw-r--r--activerecord/test/cases/dirty_test.rb15
-rw-r--r--activerecord/test/cases/helper.rb3
-rw-r--r--activerecord/test/cases/locking_test.rb19
-rw-r--r--activerecord/test/cases/mass_assignment_security_test.rb2
-rw-r--r--activerecord/test/cases/migration/column_attributes_test.rb8
-rw-r--r--activerecord/test/cases/multiple_db_test.rb4
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb38
-rw-r--r--activerecord/test/cases/relation_test.rb5
-rw-r--r--activerecord/test/cases/relations_test.rb31
-rw-r--r--activerecord/test/cases/serialization_test.rb7
-rw-r--r--activerecord/test/cases/session_store/session_test.rb81
-rw-r--r--activerecord/test/cases/session_store/sql_bypass_test.rb75
-rw-r--r--activerecord/test/cases/store_test.rb5
-rw-r--r--activerecord/test/cases/timestamp_test.rb28
-rw-r--r--activerecord/test/cases/transactions_test.rb196
-rw-r--r--activerecord/test/fixtures/friendships.yml4
-rw-r--r--activerecord/test/fixtures/people.yml3
-rw-r--r--activerecord/test/fixtures/topics.yml2
-rw-r--r--activerecord/test/models/author.rb2
-rw-r--r--activerecord/test/models/company.rb16
-rw-r--r--activerecord/test/models/friendship.rb4
-rw-r--r--activerecord/test/models/member.rb5
-rw-r--r--activerecord/test/models/person.rb2
-rw-r--r--activerecord/test/models/pirate.rb2
-rw-r--r--activerecord/test/schema/schema.rb6
-rw-r--r--activerecord/test/support/connection.rb4
-rw-r--r--activesupport/CHANGELOG.md62
-rw-r--r--activesupport/lib/active_support.rb3
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb7
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb53
-rw-r--r--activesupport/lib/active_support/callbacks.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/file/atomic.rb32
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/string/indent.rb43
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb7
-rw-r--r--activesupport/lib/active_support/dependencies.rb38
-rw-r--r--activesupport/lib/active_support/dependencies/autoload.rb60
-rw-r--r--activesupport/lib/active_support/duration.rb2
-rw-r--r--activesupport/lib/active_support/json/encoding.rb1
-rw-r--r--activesupport/lib/active_support/json/variable.rb17
-rw-r--r--activesupport/lib/active_support/locale/en.yml10
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb6
-rw-r--r--activesupport/lib/active_support/number_helper.rb161
-rw-r--r--activesupport/lib/active_support/rails.rb3
-rw-r--r--activesupport/lib/active_support/railtie.rb2
-rw-r--r--activesupport/lib/active_support/string_inquirer.rb18
-rw-r--r--activesupport/lib/active_support/tagged_logging.rb4
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb3
-rw-r--r--activesupport/test/abstract_unit.rb10
-rw-r--r--activesupport/test/autoload_test.rb (renamed from activesupport/test/autoload.rb)12
-rw-r--r--activesupport/test/autoloading_fixtures/class_folder/class_folder_subclass.rb2
-rw-r--r--activesupport/test/caching_test.rb102
-rw-r--r--activesupport/test/core_ext/file_test.rb2
-rw-r--r--activesupport/test/core_ext/module/qualified_const_test.rb29
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb56
-rw-r--r--activesupport/test/dependencies_test.rb2
-rw-r--r--activesupport/test/inflector_test.rb4
-rw-r--r--activesupport/test/json/encoding_test.rb7
-rw-r--r--activesupport/test/notifications/evented_notification_test.rb20
-rw-r--r--activesupport/test/number_helper_i18n_test.rb42
-rw-r--r--activesupport/test/number_helper_test.rb7
-rw-r--r--activesupport/test/string_inquirer_test.rb14
-rw-r--r--activesupport/test/tagged_logging_test.rb5
-rw-r--r--activesupport/test/transliterate_test.rb3
-rw-r--r--guides/code/getting_started/config/initializers/session_store.rb5
-rw-r--r--guides/rails_guides/textile_extensions.rb2
-rw-r--r--guides/source/4_0_release_notes.textile37
-rw-r--r--guides/source/action_controller_overview.textile8
-rw-r--r--guides/source/action_mailer_basics.textile22
-rw-r--r--guides/source/active_model_basics.textile12
-rw-r--r--guides/source/active_record_querying.textile3
-rw-r--r--guides/source/active_record_validations_callbacks.textile10
-rw-r--r--guides/source/active_support_core_extensions.textile45
-rw-r--r--guides/source/api_documentation_guidelines.textile2
-rw-r--r--guides/source/asset_pipeline.textile4
-rw-r--r--guides/source/association_basics.textile18
-rw-r--r--guides/source/caching_with_rails.textile4
-rw-r--r--guides/source/configuring.textile38
-rw-r--r--guides/source/contributing_to_ruby_on_rails.textile111
-rw-r--r--guides/source/engines.textile78
-rw-r--r--guides/source/i18n.textile2
-rw-r--r--guides/source/initialization.textile14
-rw-r--r--guides/source/kindle/KINDLE.md6
-rw-r--r--guides/source/kindle/welcome.html.erb2
-rw-r--r--guides/source/migrations.textile29
-rw-r--r--guides/source/performance_testing.textile3
-rw-r--r--guides/source/plugins.textile14
-rw-r--r--guides/source/routing.textile30
-rw-r--r--guides/source/security.textile4
-rw-r--r--guides/source/upgrading_ruby_on_rails.textile6
-rw-r--r--railties/CHANGELOG.md9
-rw-r--r--railties/lib/rails/application.rb9
-rw-r--r--railties/lib/rails/application/bootstrap.rb18
-rw-r--r--railties/lib/rails/application/configuration.rb29
-rw-r--r--railties/lib/rails/application/finisher.rb6
-rw-r--r--railties/lib/rails/commands.rb6
-rw-r--r--railties/lib/rails/commands/destroy.rb3
-rw-r--r--railties/lib/rails/commands/generate.rb3
-rw-r--r--railties/lib/rails/commands/server.rb9
-rw-r--r--railties/lib/rails/console/app.rb3
-rw-r--r--railties/lib/rails/engine.rb11
-rw-r--r--railties/lib/rails/engine/commands.rb4
-rw-r--r--railties/lib/rails/generators.rb1
-rw-r--r--railties/lib/rails/generators/app_base.rb4
-rw-r--r--railties/lib/rails/generators/base.rb3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb10
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environment.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt13
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt7
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/locales/en.yml22
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/routes.rb8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/humans.txt.tt7
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/session_migration/USAGE8
-rw-r--r--railties/lib/rails/generators/rails/session_migration/session_migration_generator.rb8
-rw-r--r--railties/lib/rails/railtie.rb3
-rw-r--r--railties/lib/rails/railtie/configuration.rb10
-rw-r--r--railties/lib/rails/tasks/tmp.rake12
-rw-r--r--railties/test/application/assets_test.rb26
-rw-r--r--railties/test/application/configuration_test.rb55
-rw-r--r--railties/test/application/initializers/frameworks_test.rb20
-rw-r--r--railties/test/application/initializers/hooks_test.rb8
-rw-r--r--railties/test/application/middleware/exceptions_test.rb14
-rw-r--r--railties/test/application/middleware/remote_ip_test.rb14
-rw-r--r--railties/test/application/middleware_test.rb4
-rw-r--r--railties/test/application/rake_test.rb23
-rw-r--r--railties/test/generators/app_generator_test.rb11
-rw-r--r--railties/test/generators/session_migration_generator_test.rb27
-rw-r--r--railties/test/isolation/abstract_unit.rb4
-rw-r--r--railties/test/railties/engine_test.rb48
-rw-r--r--railties/test/railties/mounted_engine_test.rb15
291 files changed, 3508 insertions, 3324 deletions
diff --git a/Gemfile b/Gemfile
index 0ce4541074..3a446f1c13 100644
--- a/Gemfile
+++ b/Gemfile
@@ -20,9 +20,9 @@ else
end
if ENV['AR_DEPRECATED_FINDERS']
- gem 'active_record_deprecated_finders', path: ENV['AR_DEPRECATED_FINDERS']
+ gem 'activerecord-deprecated_finders', path: ENV['AR_DEPRECATED_FINDERS']
else
- gem 'active_record_deprecated_finders', github: 'rails/active_record_deprecated_finders'
+ gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders'
end
# This needs to be with require false to avoid
@@ -40,7 +40,7 @@ group :doc do
end
# AS
-gem 'memcache-client', '>= 1.8.5'
+gem 'dalli'
# Add your own local bundler stuff
local_gemfile = File.dirname(__FILE__) + "/.Gemfile"
@@ -62,7 +62,7 @@ platforms :ruby do
group :db do
gem 'pg', '>= 0.11.0'
- gem 'mysql', '>= 2.8.1'
+ gem 'mysql', '>= 2.8.1' if RUBY_VERSION < '2.0.0'
gem 'mysql2', '>= 0.3.10'
end
end
@@ -96,3 +96,5 @@ end
# A gem necessary for ActiveRecord tests with IBM DB
gem 'ibm_db' if ENV['IBM_DB']
+
+gem 'benchmark-ips'
diff --git a/RELEASING_RAILS.rdoc b/RELEASING_RAILS.rdoc
index bdf0ffc2d0..af1def223a 100644
--- a/RELEASING_RAILS.rdoc
+++ b/RELEASING_RAILS.rdoc
@@ -25,10 +25,10 @@ for Rails. You can check the status of his tests here:
Do not release with Red AWDwR tests.
-=== Do we have any git dependencies? If so, contact those authors.
+=== Do we have any Git dependencies? If so, contact those authors.
-Having git dependencies indicates that we depend on unreleased code.
-Obviously rails cannot be released when it depends on unreleased code.
+Having Git dependencies indicates that we depend on unreleased code.
+Obviously Rails cannot be released when it depends on unreleased code.
Contact the authors of those particular gems and work out a release date that
suits them.
@@ -115,14 +115,14 @@ what to do in case anything goes wrong:
=== Send Rails release announcements
Write a release announcement that includes the version, changes, and links to
-github where people can find the specific commit list. Here are the mailing
+GitHub where people can find the specific commit list. Here are the mailing
lists where you should announce:
* rubyonrails-core@googlegroups.com
* rubyonrails-talk@googlegroups.com
* ruby-talk@ruby-lang.org
-Use markdown format for your announcement. Remember to ask people to report
+Use Markdown format for your announcement. Remember to ask people to report
issues with the release candidate to the rails-core mailing list.
IMPORTANT: If any users experience regressions when using the release
@@ -131,16 +131,16 @@ break existing applications.
=== Post the announcement to the Rails blog.
-If you used markdown format for your email, you can just paste it in to the
+If you used Markdown format for your email, you can just paste it in to the
blog.
* http://weblog.rubyonrails.org
-=== Post the announcement to the Rails twitter account.
+=== Post the announcement to the Rails Twitter account.
== Time between release candidate and actual release
-Check the rails-core mailing list and the github issue list for regressions in
+Check the rails-core mailing list and the GitHub issue list for regressions in
the RC.
If any regressions are found, fix the regressions and repeat the release
@@ -167,7 +167,7 @@ Today, do this stuff in this order:
* Email security lists
* Email general announcement lists
-=== Emailing the rails security announce list
+=== Emailing the Rails security announce list
Email the security announce list once for each vulnerability fixed.
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 96cfb43e0b..d2b8c35124 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -6,6 +6,22 @@
* Asynchronously send messages via the Rails Queue *Brian Cardarella*
+
+## 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/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb
index 32e6183ff6..cfbe2f1cbd 100644
--- a/actionmailer/lib/action_mailer.rb
+++ b/actionmailer/lib/action_mailer.rb
@@ -35,7 +35,10 @@ require 'active_support/lazy_load_hooks'
module ActionMailer
extend ::ActiveSupport::Autoload
- autoload :Collector
+ eager_autoload do
+ autoload :Collector
+ end
+
autoload :Base
autoload :DeliveryMethods
autoload :MailHelper
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 73ac8ea12b..900da9697e 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -683,7 +683,7 @@ module ActionMailer #:nodoc:
m.charset = charset = headers[:charset]
# Set configure delivery behavior
- wrap_delivery_behavior!(headers.delete(:delivery_method),headers.delete(:perform_deliveries))
+ wrap_delivery_behavior!(headers.delete(:delivery_method))
# Assign all headers except parts_order, content_type and body
assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path)
diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb
index 0b8d0019eb..3b38dbccc7 100644
--- a/actionmailer/lib/action_mailer/delivery_methods.rb
+++ b/actionmailer/lib/action_mailer/delivery_methods.rb
@@ -57,7 +57,7 @@ module ActionMailer
self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
end
- def wrap_delivery_behavior(mail, method=nil, perform_deliveries_header=nil) #:nodoc:
+ def wrap_delivery_behavior(mail, method=nil) #:nodoc:
method ||= self.delivery_method
mail.delivery_handler = self
@@ -74,7 +74,7 @@ module ActionMailer
mail.delivery_method(method)
end
- mail.perform_deliveries = perform_deliveries && (perform_deliveries_header.nil? || perform_deliveries_header)
+ mail.perform_deliveries = perform_deliveries
mail.raise_delivery_errors = raise_delivery_errors
end
end
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index 5c03a29f0f..8679096735 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -5,6 +5,7 @@ require "abstract_controller/railties/routes_helpers"
module ActionMailer
class Railtie < Rails::Railtie
config.action_mailer = ActiveSupport::OrderedOptions.new
+ config.eager_load_namespaces << ActionMailer
initializer "action_mailer.logger" do
ActiveSupport.on_load(:action_mailer) { self.logger ||= Rails.logger }
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 4ed332d13d..fcf5be56f7 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -493,14 +493,18 @@ class BaseTest < ActiveSupport::TestCase
end
test "assets tags should use a Mailer's asset_host settings when available" do
- ActionMailer::Base.config.asset_host = "global.com"
- ActionMailer::Base.config.assets_dir = "global/"
+ begin
+ ActionMailer::Base.config.asset_host = "http://global.com"
+ ActionMailer::Base.config.assets_dir = "global/"
- AssetMailer.asset_host = "http://local.com"
+ AssetMailer.asset_host = "http://local.com"
- mail = AssetMailer.welcome
+ mail = AssetMailer.welcome
- assert_equal(%{<img alt="Dummy" src="http://local.com/images/dummy.png" />}, mail.body.to_s.strip)
+ assert_equal(%{<img alt="Dummy" src="http://local.com/images/dummy.png" />}, mail.body.to_s.strip)
+ ensure
+ AssetMailer.asset_host = ActionMailer::Base.config.asset_host
+ end
end
# Before and After hooks
diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb
index 886e79aae9..08f84dbf3b 100644
--- a/actionmailer/test/delivery_methods_test.rb
+++ b/actionmailer/test/delivery_methods_test.rb
@@ -82,7 +82,6 @@ class MailDeliveryTest < ActiveSupport::TestCase
def welcome(hash={})
mail(DEFAULT_HEADERS.merge(hash))
end
-
end
def setup
@@ -130,18 +129,6 @@ class MailDeliveryTest < ActiveSupport::TestCase
DeliveryMailer.welcome.deliver
end
- test "does not perform deliveries if customized per instance" do
- DeliveryMailer.perform_deliveries = true
- m = DeliveryMailer.welcome(:perform_deliveries => false)
- assert_equal(false,m.perform_deliveries)
- end
-
- test "does not perform deliveries if globally set to off but instance instructs delivery" do
- DeliveryMailer.perform_deliveries = false
- m = DeliveryMailer.welcome(:perform_deliveries => true)
- assert_equal(false,m.perform_deliveries)
- end
-
test "does not append the deliveries collection if told not to perform the delivery" do
DeliveryMailer.perform_deliveries = false
DeliveryMailer.deliveries.clear
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 68cce142a2..3c8a0f48ab 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,22 +1,124 @@
## Rails 4.0.0 (unreleased) ##
+* Fix handling of date selects when using both disabled and discard options.
+ Fixes #7431.
+
+ *Vasiliy Ermolovich*
+
+* `ActiveRecord::SessionStore` is extracted out of Rails into a gem `activerecord-session_store`.
+ Setting `config.session_store` to `:active_record_store` will no longer work and will break
+ if the `activerecord-session_store` gem isn't available. *Prem Sichanugrist*
+
+* Fix select_tag when option_tags is nil.
+ Fixes #7404.
+
+ *Sandeep Ravichandran*
+
+* Add Request#formats=(extensions) that lets you set multiple formats directly in a prioritized order *DHH*
+
+ Example of using this for custom iphone views with an HTML fallback:
+
+ class ApplicationController < ActionController::Base
+ before_filter :adjust_format_for_iphone_with_html_fallback
+
+ private
+ def adjust_format_for_iphone_with_html_fallback
+ request.formats = [ :iphone, :html ] if request.env["HTTP_USER_AGENT"][/iPhone/]
+ end
+ end
+
+
+* Add Routing Concerns to declare common routes that can be reused inside
+ others resources and routes.
+
+ Code before:
+
+ resources :messages do
+ resources :comments
+ end
+
+ resources :posts do
+ resources :comments
+ resources :images, only: :index
+ end
+
+ Code after:
+
+ concern :commentable do
+ resources :comments
+ end
+
+ concern :image_attachable do
+ resources :images, only: :index
+ end
+
+ resources :messages, concerns: :commentable
+
+ resources :posts, concerns: [:commentable, :image_attachable]
+
+ *DHH + Rafael Mendonça França*
+
+* Add start_hour and end_hour options to the select_hour helper. *Evan Tann*
+
+* Raises an ArgumentError when the first argument in `form_for` contain `nil`
+ or is empty.
+
+ *Richard Schneeman*
+
+* Add 'X-Frame-Options' => 'SAMEORIGIN'
+ 'X-XSS-Protection' => '1; mode=block' and
+ 'X-Content-Type-Options' => 'nosniff'
+ as default headers.
+
+ *Egor Homakov*
+
+* Allow data attributes to be set as a first-level option for form_for, so you can write `form_for @record, data: { behavior: 'autosave' }` instead of `form_for @record, html: { data: { behavior: 'autosave' } }` *DHH*
+
+* Deprecate `button_to_function` and `link_to_function` helpers.
+
+ We recommend the use of Unobtrusive JavaScript instead. For example:
+
+ link_to "Greeting", "#", :class => "nav_link"
+
+ $(function() {
+ $('.nav_link').click(function() {
+ // Some complex code
+
+ return false;
+ });
+ });
+
+ or
+
+ link_to "Greeting", '#', onclick: "alert('Hello world!'); return false", class: "nav_link"
+
+ for simple cases.
+
+ *Rafael Mendonça França*
+
+* `javascript_include_tag :all` will now not include `application.js` if the file does not exists. *Prem Sichanugrist*
+
+* Send an empty response body when call `head` with status between 100 and 199, 204, 205 or 304.
+
+ *Armand du Plessis*
+
* Fixed issue with where Digest authentication would not work behind a proxy. *Arthur Smith*
* Added ActionController::Live. Mix it in to your controller and you can
stream data to the client live. For example:
- class FooController < ActionController::Base
- include ActionController::Live
+ class FooController < ActionController::Base
+ include ActionController::Live
- def index
- 100.times {
- # Client will see this as it's written
- response.stream.write "hello world\n"
- sleep 1
- }
- response.stream.close
+ def index
+ 100.times {
+ # Client will see this as it's written
+ response.stream.write "hello world\n"
+ sleep 1
+ }
+ response.stream.close
+ end
end
- end
* Remove ActionDispatch::Head middleware in favor of Rack::Head. *Santiago Pastorino*
@@ -140,8 +242,6 @@
* Replace `include_seconds` boolean argument with `:include_seconds => true` option
in `distance_of_time_in_words` and `time_ago_in_words` signature. *Dmitriy Kiriyenko*
-* Remove `button_to_function` and `link_to_function` helpers. *Rafael Mendonça França*
-
* Make current object and counter (when it applies) variables accessible when
rendering templates with :object / :collection. *Carlos Antonio da Silva*
@@ -227,13 +327,13 @@
* Add `collection_check_boxes` form helper, similar to `collection_select`:
Example:
- collection_check_boxes :post, :author_ids, Author.all, :id, :name
- # Outputs something like:
- <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" />
- <label for="post_author_ids_1">D. Heinemeier Hansson</label>
- <input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
- <label for="post_author_ids_2">D. Thomas</label>
- <input name="post[author_ids][]" type="hidden" value="" />
+ collection_check_boxes :post, :author_ids, Author.all, :id, :name
+ # Outputs something like:
+ <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" />
+ <label for="post_author_ids_1">D. Heinemeier Hansson</label>
+ <input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
+ <label for="post_author_ids_2">D. Thomas</label>
+ <input name="post[author_ids][]" type="hidden" value="" />
The label/check_box pairs can be customized with a block.
@@ -242,12 +342,12 @@
* Add `collection_radio_buttons` form helper, similar to `collection_select`:
Example:
- collection_radio_buttons :post, :author_id, Author.all, :id, :name
- # Outputs something like:
- <input id="post_author_id_1" name="post[author_id]" type="radio" value="1" />
- <label for="post_author_id_1">D. Heinemeier Hansson</label>
- <input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
- <label for="post_author_id_2">D. Thomas</label>
+ collection_radio_buttons :post, :author_id, Author.all, :id, :name
+ # Outputs something like:
+ <input id="post_author_id_1" name="post[author_id]" type="radio" value="1" />
+ <label for="post_author_id_1">D. Heinemeier Hansson</label>
+ <input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
+ <label for="post_author_id_2">D. Thomas</label>
The label/radio_button pairs can be customized with a block.
@@ -291,6 +391,67 @@
HTML5 `mark` element. *Brian Cardarella*
+## Rails 3.2.8 (Aug 9, 2012) ##
+
+* There is an XSS vulnerability in the strip_tags helper in Ruby on Rails, the
+ helper doesn't correctly handle malformed html. As a result an attacker can
+ execute arbitrary javascript through the use of specially crafted malformed
+ html.
+
+ *Marek from Nethemba (www.nethemba.com) & Santiago Pastorino*
+
+* When a "prompt" value is supplied to the `select_tag` helper, the "prompt" value is not escaped.
+ If untrusted data is not escaped, and is supplied as the prompt value, there is a potential for XSS attacks.
+ Vulnerable code will look something like this:
+ select_tag("name", options, :prompt => UNTRUSTED_INPUT)
+
+ *Santiago Pastorino*
+
+* Reverted the deprecation of `:confirm`. *Rafael Mendonça França*
+
+* Reverted the deprecation of `:disable_with`. *Rafael Mendonça França*
+
+* Reverted the deprecation of `:mouseover` option to `image_tag`. *Rafael Mendonça França*
+
+* Reverted the deprecation of `button_to_function` and `link_to_function` helpers.
+
+ *Rafael Mendonça França*
+
+
+## Rails 3.2.7 (Jul 26, 2012) ##
+
+* Do not convert digest auth strings to symbols. CVE-2012-3424
+
+* Bump Journey requirements to 1.0.4
+
+* Add support for optional root segments containing slashes
+
+* Fixed bug creating invalid HTML in select options
+
+* Show in log correct wrapped keys
+
+* Fix NumberHelper options wrapping to prevent verbatim blocks being rendered instead of line continuations.
+
+* ActionController::Metal doesn't have logger method, check it and then delegate
+
+* ActionController::Caching depends on RackDelegation and AbstractController::Callbacks
+
+
+## Rails 3.2.6 (Jun 12, 2012) ##
+
+* nil is removed from array parameter values
+
+ CVE-2012-2694
+
+* Deprecate `:confirm` in favor of `':data => { :confirm => "Text" }'` option for `button_to`, `button_tag`, `image_submit_tag`, `link_to` and `submit_tag` helpers.
+
+ *Carlos Galdino*
+
+* Allow to use mounted_helpers (helpers for accessing mounted engines) in ActionView::TestCase. *Piotr Sarnacki*
+
+* Include mounted_helpers (helpers for accessing mounted engines) in ActionDispatch::IntegrationTest by default. *Piotr Sarnacki*
+
+
## Rails 3.2.5 (Jun 1, 2012) ##
* No changes.
@@ -854,11 +1015,11 @@
Before:
- translate('foo_html', :something => '<script>') # => "...<script>..."
+ translate('foo_html', :something => '<script>') # => "...<script>..."
After:
- translate('foo_html', :something => '<script>') # => "...&lt;script&gt;..."
+ translate('foo_html', :something => '<script>') # => "...&lt;script&gt;..."
*Sergey Nartimov*
diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc
index 1fdc57e14d..ccd0193515 100644
--- a/actionpack/README.rdoc
+++ b/actionpack/README.rdoc
@@ -28,291 +28,6 @@ by default and Action View rendering is implicitly triggered by Action
Controller. However, these modules are designed to function on their own and
can be used outside of Rails.
-A short rundown of some of the major features:
-
-* Actions grouped in controller as methods instead of separate command objects
- and can therefore share helper methods
-
- class CustomersController < ActionController::Base
- def show
- @customer = find_customer
- end
-
- def update
- @customer = find_customer
- if @customer.update_attributes(params[:customer])
- redirect_to :action => "show"
- else
- render :action => "edit"
- end
- end
-
- private
- def find_customer
- Customer.find params[:id]
- end
- end
-
- {Learn more}[link:classes/ActionController/Base.html]
-
-
-* ERB templates (static content mixed with dynamic output from ruby)
-
- <% @posts.each do |post| %>
- Title: <%= post.title %>
- <% end %>
-
- All post titles: <%= @posts.collect{ |p| p.title }.join(", ") %>
-
- <% unless @person.is_client? %>
- Not for clients to see...
- <% end %>
-
- {Learn more}[link:classes/ActionView.html]
-
-
-* "Builder" templates (great for XML content, like RSS)
-
- xml.rss("version" => "2.0") do
- xml.channel do
- xml.title(@feed_title)
- xml.link(@url)
- xml.description "Basecamp: Recent items"
- xml.language "en-us"
- xml.ttl "40"
-
- @recent_items.each do |item|
- xml.item do
- xml.title(item_title(item))
- xml.description(item_description(item))
- xml.pubDate(item_pubDate(item))
- xml.guid(@recent_items.url(item))
- xml.link(@recent_items.url(item))
- end
- end
- end
- end
-
- {Learn more}[link:classes/ActionView/Base.html]
-
-
-* Filters for pre- and post-processing of the response
-
- class WeblogController < ActionController::Base
- # filters as methods
- before_filter :authenticate, :cache, :audit
-
- # filter as a proc
- after_filter { |c| c.response.body = Gzip::compress(c.response.body) }
-
- # class filter
- after_filter LocalizeFilter
-
- def index
- # Before this action is run, the user will be authenticated, the cache
- # will be examined to see if a valid copy of the results already
- # exists, and the action will be logged for auditing.
-
- # After this action has run, the output will first be localized then
- # compressed to minimize bandwidth usage
- end
-
- private
- def authenticate
- # Implement the filter with full access to both request and response
- end
- end
-
- {Learn more}[link:classes/ActionController/Filters/ClassMethods.html]
-
-
-* Helpers for forms, dates, action links, and text
-
- <%= text_field_tag "post", "title", "size" => 30 %>
- <%= link_to "New post", :controller => "post", :action => "new" %>
- <%= truncate(post.title, :length => 25) %>
-
- {Learn more}[link:classes/ActionView/Helpers.html]
-
-
-* Layout sharing for template reuse
-
- class WeblogController < ActionController::Base
- layout "weblog_layout"
-
- def hello_world
- end
- end
-
- Layout file (called weblog_layout):
- <html><body><%= yield %></body></html>
-
- Template for hello_world action:
- <h1>Hello world</h1>
-
- Result of running hello_world action:
- <html><body><h1>Hello world</h1></body></html>
-
- {Learn more}[link:classes/ActionController/Layout/ClassMethods.html]
-
-
-* Routing makes pretty URLs incredibly easy
-
- match 'clients/:client_name/:project_name/:controller/:action'
-
- Accessing "/clients/37signals/basecamp/project/index" calls ProjectController#index with
- { "client_name" => "37signals", "project_name" => "basecamp" } in `params`
-
- From that action, you can write the redirect in a number of ways:
-
- redirect_to(:action => "edit") =>
- /clients/37signals/basecamp/project/edit
-
- redirect_to(:client_name => "nextangle", :project_name => "rails") =>
- /clients/nextangle/rails/project/index
-
- {Learn more}[link:classes/ActionDispatch/Routing.html]
-
-
-* Easy testing of both controller and rendered template through ActionController::TestCase
-
- class LoginControllerTest < ActionController::TestCase
- def test_failing_authenticate
- process :authenticate, :user_name => "nop", :password => ""
- assert flash.has_key?(:alert)
- assert_redirected_to :action => "index"
- end
- end
-
- {Learn more}[link:classes/ActionController/TestCase.html]
-
-
-* Automated benchmarking and integrated logging
-
- Started GET "/weblog" for 127.0.0.1 at Fri May 28 00:41:55
- Processing by WeblogController#index as HTML
- Rendered weblog/index.html.erb within layouts/application (25.7ms)
- Completed 200 OK in 29.3ms
-
- If Active Record is used as the model, you'll have the database debugging
- as well:
-
- Started POST "/posts" for 127.0.0.1 at Sat Jun 19 14:04:23
- Processing by PostsController#create as HTML
- Parameters: {"post"=>{"title"=>"this is good"}}
- SQL (0.6ms) INSERT INTO posts (title) VALUES('this is good')
- Redirected to http://example.com/posts/5
- Completed 302 Found in 221ms (Views: 215ms | ActiveRecord: 0.6ms)
-
- You specify a logger through a class method, such as:
-
- ActionController::Base.logger = ActiveSupport::Logger.new("Application Log")
- ActionController::Base.logger = Log4r::Logger.new("Application Log")
-
-
-* Caching at three levels of granularity (page, action, fragment)
-
- class WeblogController < ActionController::Base
- caches_page :show
- caches_action :account
-
- def show
- # the output of the method will be cached as
- # ActionController::Base.page_cache_directory + "/weblog/show.html"
- # and the web server will pick it up without even hitting Rails
- end
-
- def account
- # the output of the method will be cached in the fragment store
- # but Rails is hit to retrieve it, so filters are run
- end
-
- def update
- List.update(params[:list][:id], params[:list])
- expire_page :action => "show", :id => params[:list][:id]
- expire_action :action => "account"
- redirect_to :action => "show", :id => params[:list][:id]
- end
- end
-
- {Learn more}[link:classes/ActionController/Caching.html]
-
-
-* Powerful debugging mechanism for local requests
-
- All exceptions raised on actions performed on the request of a local user
- will be presented with a tailored debugging screen that includes exception
- message, stack trace, request parameters, session contents, and the
- half-finished response.
-
- {Learn more}[link:classes/ActionController/Rescue.html]
-
-
-== Simple example (from outside of Rails)
-
-This example will implement a simple weblog system using inline templates and
-an Active Record model. So let's build that WeblogController with just a few
-methods:
-
- require 'action_controller'
- require 'post'
-
- class WeblogController < ActionController::Base
- layout "weblog/layout"
-
- def index
- @posts = Post.all
- end
-
- def show
- @post = Post.find(params[:id])
- end
-
- def new
- @post = Post.new
- end
-
- def create
- @post = Post.create(params[:post])
- redirect_to :action => "show", :id => @post.id
- end
- end
-
- WeblogController::Base.view_paths = [ File.dirname(__FILE__) ]
- WeblogController.process_cgi if $0 == __FILE__
-
-The last two lines are responsible for telling ActionController where the
-template files are located and actually running the controller on a new
-request from the web-server (e.g., Apache).
-
-And the templates look like this:
-
- weblog/layout.html.erb:
- <html><body>
- <%= yield %>
- </body></html>
-
- weblog/index.html.erb:
- <% @posts.each do |post| %>
- <p><%= link_to(post.title, :action => "show", :id => post.id) %></p>
- <% end %>
-
- weblog/show.html.erb:
- <p>
- <b><%= @post.title %></b><br/>
- <b><%= @post.content %></b>
- </p>
-
- weblog/new.html.erb:
- <%= form "post" %>
-
-This simple setup will list all the posts in the system on the index page,
-which is called by accessing /weblog/. It uses the form builder for the Active
-Record model to make the new screen, which in turn hands everything over to
-the create action (that's the default target for the form builder when given a
-new model). After creating the post, it'll redirect to the show page using
-an URL such as /weblog/5 (where 5 is the id of the post).
-
== Download and installation
diff --git a/actionpack/examples/performance.rb b/actionpack/examples/performance.rb
deleted file mode 100644
index 8ea4758961..0000000000
--- a/actionpack/examples/performance.rb
+++ /dev/null
@@ -1,185 +0,0 @@
-ENV['RAILS_ENV'] ||= 'production'
-
-require File.expand_path('../../../load_paths', __FILE__)
-require 'action_pack'
-require 'action_controller'
-require 'action_view'
-require 'active_model'
-require 'benchmark'
-
-MyHash = Class.new(Hash)
-
-Hash.class_eval do
- extend ActiveModel::Naming
- include ActiveModel::Conversion
-end
-
-class Runner
- def initialize(app, output)
- @app, @output = app, output
- end
-
- def puts(*)
- super if @output
- end
-
- def call(env)
- env['n'].to_i.times { @app.call(env) }
- @app.call(env).tap { |response| report(env, response) }
- end
-
- def report(env, response)
- return unless ENV["DEBUG"]
- out = env['rack.errors']
- out.puts response[0], response[1].to_yaml, '---'
- response[2].each { |part| out.puts part }
- out.puts '---'
- end
-
- def self.puts(*)
- super if @output
- end
-
- def self.print(*)
- super if @output
- end
-
- def self.app_and_env_for(action, n)
- env = Rack::MockRequest.env_for("/")
- env.merge!('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout)
- app = lambda { |env| BasePostController.action(action).call(env) }
- return app, env
- end
-
- $ran = []
-
- def self.run(action, n, output = true)
- print "."
- STDOUT.flush
- @output = output
- label = action.to_s
- app, env = app_and_env_for(action, n)
- t = Benchmark.realtime { new(app, output).call(env) }
- $ran << [label, (t * 1000).to_i.to_s] if output
- end
-
- def self.done
- puts
- header, content = "", ""
- $ran.each do |k,v|
- size = [k.size, v.size].max + 1
- header << format("%#{size}s", k)
- content << format("%#{size}s", v)
- end
- puts header
- puts content
- end
-end
-
-ActionController::Base.logger = nil
-ActionController::Base.config.compile_methods!
-ActionView::Resolver.caching = ENV["RAILS_ENV"] == "production"
-
-class BasePostController < ActionController::Base
- append_view_path "#{File.dirname(__FILE__)}/views"
-
- def overhead
- self.response_body = ''
- end
-
- def index
- render :text => ''
- end
-
- $OBJECT = {:name => "Hello my name is omg", :address => "333 omg"}
-
- def partial
- render :partial => "/collection", :object => $OBJECT
- end
-
- def partial_10
- render :partial => "/ten_partials"
- end
-
- def partial_100
- render :partial => "/hundred_partials"
- end
-
- $COLLECTION1 = []
- 10.times do |i|
- $COLLECTION1 << { :name => "Hello my name is omg", :address => "333 omg" }
- end
-
- def coll_10
- render :partial => "/collection", :collection => $COLLECTION1
- end
-
- $COLLECTION2 = []
- 100.times do |i|
- $COLLECTION2 << { :name => "Hello my name is omg", :address => "333 omg" }
- end
-
- def coll_100
- render :partial => "/collection", :collection => $COLLECTION2
- end
-
- def uniq_100
- render :partial => $COLLECTION2
- end
-
- $COLLECTION3 = []
- 50.times do |i|
- $COLLECTION3 << {:name => "Hello my name is omg", :address => "333 omg"}
- $COLLECTION3 << MyHash.new(:name => "Hello my name is omg", :address => "333 omg")
- end
-
- def diff_100
- render :partial => $COLLECTION3
- end
-
- def template_1
- render :template => "template"
- end
-
- module Foo
- def omg
- "omg"
- end
- end
-
- helper Foo
-end
-
-N = (ENV['N'] || 1000).to_i
-# ActionController::Base.use_accept_header = false
-
-def run_all!(times, verbose)
- Runner.run(:overhead, times, verbose)
- Runner.run(:index, times, verbose)
- Runner.run(:template_1, times, verbose)
- Runner.run(:partial, times, verbose)
- Runner.run(:partial_10, times, verbose)
- Runner.run(:coll_10, times, verbose)
- Runner.run(:partial_100, times, verbose)
- Runner.run(:coll_100, times, verbose)
- Runner.run(:uniq_100, times, verbose)
- Runner.run(:diff_100, times, verbose)
-end
-
-if ENV["PROFILE"]
- Runner.run(ENV["PROFILE"].to_sym, 1, false)
- require "ruby-prof"
- RubyProf.start
- Runner.run(ENV["PROFILE"].to_sym, N, true)
- result = RubyProf.stop
- printer = RubyProf::CallStackPrinter.new(result)
- printer.print(File.open("output.html", "w"))
-else
- run_all!(1, false)
-
- (ENV["M"] || 1).to_i.times do
- $ran = []
- run_all!(N, true)
- Runner.done
- end
-end \ No newline at end of file
diff --git a/actionpack/examples/views/_collection.erb b/actionpack/examples/views/_collection.erb
deleted file mode 100644
index cee3fe64c0..0000000000
--- a/actionpack/examples/views/_collection.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<%= collection[:name] %>
-<%= collection[:address] %>
-<%= omg %> \ No newline at end of file
diff --git a/actionpack/examples/views/_hello.erb b/actionpack/examples/views/_hello.erb
deleted file mode 100644
index 5ab2f8a432..0000000000
--- a/actionpack/examples/views/_hello.erb
+++ /dev/null
@@ -1 +0,0 @@
-Hello \ No newline at end of file
diff --git a/actionpack/examples/views/_hundred_partials.erb b/actionpack/examples/views/_hundred_partials.erb
deleted file mode 100644
index 35c2a6c9d3..0000000000
--- a/actionpack/examples/views/_hundred_partials.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<% 100.times do %>
- <%= render :partial => "/collection", :object => $OBJECT %>
-<% end %> \ No newline at end of file
diff --git a/actionpack/examples/views/_partial.erb b/actionpack/examples/views/_partial.erb
deleted file mode 100644
index 3ca8e80b52..0000000000
--- a/actionpack/examples/views/_partial.erb
+++ /dev/null
@@ -1,10 +0,0 @@
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
-<%= "Hello" %>
diff --git a/actionpack/examples/views/_ten_partials.erb b/actionpack/examples/views/_ten_partials.erb
deleted file mode 100644
index fd02991e22..0000000000
--- a/actionpack/examples/views/_ten_partials.erb
+++ /dev/null
@@ -1,10 +0,0 @@
-<%= render :partial => '/collection', :object => $OBJECT %>
-<%= render :partial => '/collection', :object => $OBJECT %>
-<%= render :partial => '/collection', :object => $OBJECT %>
-<%= render :partial => '/collection', :object => $OBJECT %>
-<%= render :partial => '/collection', :object => $OBJECT %>
-<%= render :partial => '/collection', :object => $OBJECT %>
-<%= render :partial => '/collection', :object => $OBJECT %>
-<%= render :partial => '/collection', :object => $OBJECT %>
-<%= render :partial => '/collection', :object => $OBJECT %>
-<%= render :partial => '/collection', :object => $OBJECT %> \ No newline at end of file
diff --git a/actionpack/examples/views/hashes/_hash.erb b/actionpack/examples/views/hashes/_hash.erb
deleted file mode 100644
index c100a290bd..0000000000
--- a/actionpack/examples/views/hashes/_hash.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<%= hash[:name] %>
-<%= hash[:address] %>
-<%= omg %> \ No newline at end of file
diff --git a/actionpack/examples/views/my_hashes/_my_hash.erb b/actionpack/examples/views/my_hashes/_my_hash.erb
deleted file mode 100644
index e25d84101a..0000000000
--- a/actionpack/examples/views/my_hashes/_my_hash.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<%= my_hash[:name] %>
-<%= my_hash[:address] %>
-<%= omg %> \ No newline at end of file
diff --git a/actionpack/examples/views/template.html.erb b/actionpack/examples/views/template.html.erb
deleted file mode 100644
index 5ab2f8a432..0000000000
--- a/actionpack/examples/views/template.html.erb
+++ /dev/null
@@ -1 +0,0 @@
-Hello \ No newline at end of file
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index ceb90f8cee..31df9d605c 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -48,6 +48,12 @@ module ActionController
eager_autoload do
autoload :RecordIdentifier
end
+
+ def self.eager_load!
+ super
+ ActionController::Caching.eager_load!
+ HTML.eager_load!
+ end
end
# All of these simply register additional autoloads
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 71425cd542..f829f5e8a2 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -88,15 +88,6 @@ module ActionController
#
# Do not put secret information in cookie-based sessions!
#
- # Other options for session storage:
- #
- # * ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and,
- # unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set
- #
- # MyApplication::Application.config.session_store :active_record_store
- #
- # in your <tt>config/initializers/session_store.rb</tt> and run <tt>script/rails g session_migration</tt>.
- #
# == Responses
#
# Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index abeb49d16f..9c77b0ccf4 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -5,48 +5,18 @@ module ActionController #:nodoc:
# useful when certain elements of an action change frequently or
# depend on complicated state while other parts rarely change or
# can be shared amongst multiple parties. The caching is done using
- # the <tt>cache</tt> helper available in the Action View. A
- # template with fragment caching might look like:
+ # the <tt>cache</tt> helper available in the Action View. See
+ # ActionView::Helpers::CacheHelper for more information.
#
- # <b>Hello <%= @name %></b>
+ # While it's strongly recommended that you use key-based cache
+ # expiration (see links in CacheHelper for more information),
+ # it is also possible to manually expire caches. For example:
#
- # <% cache do %>
- # All the topics in the system:
- # <%= render :partial => "topic", :collection => Topic.all %>
- # <% end %>
- #
- # This cache will bind the name of the action that called it, so if
- # this code was part of the view for the topics/list action, you
- # would be able to invalidate it using:
- #
- # expire_fragment(:controller => "topics", :action => "list")
- #
- # This default behavior is limited if you need to cache multiple
- # fragments per action or if the action itself is cached using
- # <tt>caches_action</tt>. To remedy this, there is an option to
- # qualify the name of the cached fragment by using the
- # <tt>:action_suffix</tt> option:
- #
- # <% cache(:action => "list", :action_suffix => "all_topics") do %>
- #
- # That would result in a name such as
- # <tt>/topics/list/all_topics</tt>, avoiding conflicts with the
- # action cache and with any fragments that use a different suffix.
- # Note that the URL doesn't have to really exist or be callable
- # - the url_for system is just used to generate unique cache names
- # that we can refer to when we need to expire the cache.
- #
- # The expiration call for this example is:
- #
- # expire_fragment(:controller => "topics",
- # :action => "list",
- # :action_suffix => "all_topics")
+ # expire_fragment("name_of_cache")
module Fragments
# Given a key (as described in <tt>expire_fragment</tt>), returns
# a key suitable for use in reading, writing, or expiring a
- # cached fragment. If the key is a hash, the generated key is the
- # return value of url_for on that hash (without the protocol).
- # All keys are prefixed with <tt>views/</tt> and uses
+ # cached fragment. All keys are prefixed with <tt>views/</tt> and uses
# ActiveSupport::Cache.expand_cache_key for the expansion.
def fragment_cache_key(key)
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb
index 379ff97048..5422cb93c4 100644
--- a/actionpack/lib/action_controller/metal/data_streaming.rb
+++ b/actionpack/lib/action_controller/metal/data_streaming.rb
@@ -76,8 +76,8 @@ module ActionController #:nodoc:
end
# Avoid having to pass an open file handle as the response body.
- # Rack::Sendfile will usually intercepts the response and just uses
- # the path directly, so no reason to open the file.
+ # Rack::Sendfile will usually intercept the response and uses
+ # the path directly, so there is no reason to open the file.
class FileBody #:nodoc:
attr_reader :to_path
diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb
index 77d799a38a..e905a3cf1d 100644
--- a/actionpack/lib/action_controller/metal/force_ssl.rb
+++ b/actionpack/lib/action_controller/metal/force_ssl.rb
@@ -32,7 +32,7 @@ module ActionController
# ==== Options
# * <tt>host</tt> - Redirect to a different host name
# * <tt>only</tt> - The callback should be run only for this action
- # * <tt>except<tt> - The callback should be run for all actions except this action
+ # * <tt>except</tt> - The callback should be run for all actions except this action
# * <tt>if</tt> - A symbol naming an instance method or a proc; the callback
# will be called only when it returns a true value.
# * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback
diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb
index 43a9e3aa9d..32e5afa335 100644
--- a/actionpack/lib/action_controller/metal/live.rb
+++ b/actionpack/lib/action_controller/metal/live.rb
@@ -33,13 +33,13 @@ module ActionController
module Live
class Buffer < ActionDispatch::Response::Buffer #:nodoc:
def initialize(response)
- super(response, Queue.new)
+ super(response, SizedQueue.new(10))
end
def write(string)
unless @response.committed?
@response.headers["Cache-Control"] = "no-cache"
- @response.headers.delete("Content-Length")
+ @response.headers.delete "Content-Length"
end
super
@@ -47,13 +47,13 @@ module ActionController
def each
while str = @buf.pop
- yield(str)
+ yield str
end
end
def close
super
- @buf.push(nil)
+ @buf.push nil
end
end
@@ -72,16 +72,15 @@ module ActionController
super
end
+ def merge(other)
+ self.class.new @response, __getobj__.merge(other)
+ end
+
def to_hash
__getobj__.dup
end
end
- def initialize(status = 200, header = {}, body = [])
- header = Header.new(self, header)
- super(status, header, body)
- end
-
def commit!
headers.freeze
super
@@ -89,11 +88,15 @@ module ActionController
private
- def build_buffer(response, body)
- buf = Live::Buffer.new(response)
- body.each { |part| buf.write(part) }
- buf
- end
+ def build_buffer(response, body)
+ buf = Live::Buffer.new response
+ body.each { |part| buf.write part }
+ buf
+ end
+
+ def merge_default_headers(original, default)
+ Header.new self, super
+ end
end
def process(name)
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 64461c73e0..18ae2c3bfc 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -341,9 +341,9 @@ module ActionController #:nodoc:
config = self.class.mimes_for_respond_to[mime]
if config[:except]
- !action.in?(config[:except])
+ !config[:except].include?(action)
elsif config[:only]
- action.in?(config[:only])
+ config[:only].include?(action)
else
true
end
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index dd5ceb3478..0cdd17bc2e 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -30,9 +30,15 @@ module ActionController
:_recall => request.symbolized_path_parameters
).freeze
- if _routes.equal?(env["action_dispatch.routes"])
+ if (same_origin = _routes.equal?(env["action_dispatch.routes"])) ||
+ (script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) ||
+ (original_script_name = env['SCRIPT_NAME'])
@_url_options.dup.tap do |options|
- options[:script_name] = request.script_name.dup
+ if original_script_name
+ options[:original_script_name] = original_script_name
+ else
+ options[:script_name] = same_origin ? request.script_name.dup : script_name
+ end
options.freeze
end
else
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 851a2c4aee..3ecc105e22 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -9,6 +9,8 @@ module ActionController
class Railtie < Rails::Railtie #:nodoc:
config.action_controller = ActiveSupport::OrderedOptions.new
+ config.eager_load_namespaces << ActionController
+
initializer "action_controller.assets_config", :group => :all do |app|
app.config.action_controller.assets_dir ||= app.config.paths["public"].first
end
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index cc81970ddc..57b4678add 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -26,7 +26,6 @@ require 'active_support/rails'
require 'active_support/core_ext/module/attribute_accessors'
require 'action_pack'
-require 'active_model'
require 'rack'
module Rack
@@ -39,9 +38,11 @@ module ActionDispatch
class IllegalStateError < StandardError
end
- autoload_under 'http' do
- autoload :Request
- autoload :Response
+ eager_autoload do
+ autoload_under 'http' do
+ autoload :Request
+ autoload :Response
+ end
end
autoload_under 'middleware' do
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index e31f3b823d..0f98e84788 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -80,6 +80,27 @@ module ActionDispatch
@env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
end
+ # Sets the \formats by string extensions. This differs from #format= by allowing you
+ # to set multiple, ordered formats, which is useful when you want to have a fallback.
+ #
+ # In this example, the :iphone format will be used if it's available, otherwise it'll fallback
+ # to the :html format.
+ #
+ # class ApplicationController < ActionController::Base
+ # before_filter :adjust_format_for_iphone_with_html_fallback
+ #
+ # private
+ # def adjust_format_for_iphone_with_html_fallback
+ # request.formats = [ :iphone, :html ] if request.env["HTTP_USER_AGENT"][/iPhone/]
+ # end
+ # end
+ def formats=(extensions)
+ parameters[:format] = extensions.first.to_s
+ @env["action_dispatch.request.formats"] = extensions.collect do |extension|
+ Mime::Type.lookup_by_extension(extension)
+ end
+ end
+
# Receives an array of mimes and return the first user sent mime that
# matches the order array.
#
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index bcfd0b0d00..9a7b5bc8c7 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -4,6 +4,11 @@ require 'active_support/core_ext/hash/indifferent_access'
module ActionDispatch
module Http
module Parameters
+ def initialize(env)
+ super
+ @symbolized_path_params = nil
+ end
+
# Returns both GET and POST \parameters in a single hash.
def parameters
@env["action_dispatch.request.parameters"] ||= begin
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 1377e53ce8..d24c7c7f3f 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -38,6 +38,17 @@ module ActionDispatch
METHOD
end
+ def initialize(env)
+ super
+ @method = nil
+ @request_method = nil
+ @remote_ip = nil
+ @original_fullpath = nil
+ @fullpath = nil
+ @ip = nil
+ @uuid = nil
+ end
+
def key?(key)
@env.key?(key)
end
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index d336808e7c..11b7534ea4 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -58,6 +58,7 @@ module ActionDispatch # :nodoc:
LOCATION = "Location".freeze
cattr_accessor(:default_charset) { "utf-8" }
+ cattr_accessor(:default_headers)
include Rack::Response::Helpers
include ActionDispatch::Http::Cache::Response
@@ -96,6 +97,8 @@ module ActionDispatch # :nodoc:
def initialize(status = 200, header = {}, body = [])
super()
+ header = merge_default_headers(header, self.class.default_headers)
+
self.body, self.header, self.status = body, header, status
@sending_file = false
@@ -238,6 +241,12 @@ module ActionDispatch # :nodoc:
private
+ def merge_default_headers(original, default)
+ return original unless default.respond_to?(:merge)
+
+ default.merge(original)
+ end
+
def build_buffer(response, body)
Buffer.new response, body
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 4266ec042e..8aa02ec482 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -87,6 +87,12 @@ module ActionDispatch
end
end
+ def initialize(env)
+ super
+ @protocol = nil
+ @port = nil
+ end
+
# Returns the complete URL used for this request.
def url
protocol + host_with_port + fullpath
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index 62f906219c..ccc0435a39 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -19,10 +19,19 @@ module ActionDispatch
:verbose => false
}
+ config.action_dispatch.default_headers = {
+ 'X-Frame-Options' => 'SAMEORIGIN',
+ 'X-XSS-Protection' => '1; mode=block',
+ 'X-Content-Type-Options' => 'nosniff'
+ }
+
+ config.eager_load_namespaces << ActionDispatch
+
initializer "action_dispatch.configure" do |app|
ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
ActionDispatch::Response.default_charset = app.config.action_dispatch.default_charset || app.config.encoding
+ ActionDispatch::Response.default_headers = app.config.action_dispatch.default_headers
ActionDispatch::ExceptionWrapper.rescue_responses.merge!(config.action_dispatch.rescue_responses)
ActionDispatch::ExceptionWrapper.rescue_templates.merge!(config.action_dispatch.rescue_templates)
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 5e2f1ff1e0..f64cff8394 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -444,9 +444,10 @@ module ActionDispatch
raise "A rack application must be specified" unless path
- options[:as] ||= app_name(app)
+ options[:as] ||= app_name(app)
+ options[:via] ||= :all
- match(path, options.merge(:to => app, :anchor => false, :format => false, :via => :all))
+ match(path, options.merge(:to => app, :anchor => false, :format => false))
define_generate_prefix(app, options[:as])
self
@@ -909,7 +910,7 @@ module ActionDispatch
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
# a path appended since they fit properly in their scope level.
VALID_ON_OPTIONS = [:new, :collection, :member]
- RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param]
+ RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
CANONICAL_ACTIONS = %w(index create new show update destroy)
class Resource #:nodoc:
@@ -1046,6 +1047,8 @@ module ActionDispatch
resource_scope(:resource, SingletonResource.new(resources.pop, options)) do
yield if block_given?
+ concerns(options[:concerns]) if options[:concerns]
+
collection do
post :create
end if parent_resource.actions.include?(:create)
@@ -1210,6 +1213,8 @@ module ActionDispatch
resource_scope(:resources, Resource.new(resources.pop, options)) do
yield if block_given?
+ concerns(options[:concerns]) if options[:concerns]
+
collection do
get :index if parent_resource.actions.include?(:index)
post :create if parent_resource.actions.include?(:create)
@@ -1580,15 +1585,71 @@ module ActionDispatch
end
end
+ # Routing Concerns allows you to declare common routes that can be reused
+ # inside others resources and routes.
+ #
+ # concern :commentable do
+ # resources :comments
+ # end
+ #
+ # concern :image_attachable do
+ # resources :images, only: :index
+ # end
+ #
+ # These concerns are used in Resources routing:
+ #
+ # resources :messages, concerns: [:commentable, :image_attachable]
+ #
+ # or in a scope or namespace:
+ #
+ # namespace :posts do
+ # concerns :commentable
+ # end
+ module Concerns
+ # Define a routing concern using a name.
+ #
+ # concern :commentable do
+ # resources :comments
+ # end
+ #
+ # Any routing helpers can be used inside a concern.
+ def concern(name, &block)
+ @concerns[name] = block
+ end
+
+ # Use the named concerns
+ #
+ # resources :posts do
+ # concerns :commentable
+ # end
+ #
+ # concerns also work in any routes helper that you want to use:
+ #
+ # namespace :posts do
+ # concerns :commentable
+ # end
+ def concerns(*names)
+ names.flatten.each do |name|
+ if concern = @concerns[name]
+ instance_eval(&concern)
+ else
+ raise ArgumentError, "No concern named #{name} was found!"
+ end
+ end
+ end
+ end
+
def initialize(set) #:nodoc:
@set = set
@scope = { :path_names => @set.resources_path_names }
+ @concerns = {}
end
include Base
include HttpHelpers
include Redirection
include Scoping
+ include Concerns
include Resources
end
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 62c921ff97..32d267d1d6 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -163,9 +163,9 @@ module ActionDispatch
private
def define_named_route_methods(name, route)
- define_url_helper route, :"#{name}_path",
+ define_url_helper route, :"#{name}_path",
route.defaults.merge(:use_route => name, :only_path => true)
- define_url_helper route, :"#{name}_url",
+ define_url_helper route, :"#{name}_url",
route.defaults.merge(:use_route => name, :only_path => false)
end
@@ -226,7 +226,7 @@ module ActionDispatch
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
attr_accessor :disable_clear_and_finalize, :resources_path_names
- attr_accessor :default_url_options, :request_class, :valid_conditions
+ attr_accessor :default_url_options, :request_class
alias :routes :set
@@ -238,13 +238,7 @@ module ActionDispatch
self.named_routes = NamedRouteCollection.new
self.resources_path_names = self.class.default_resources_path_names.dup
self.default_url_options = {}
-
self.request_class = request_class
- @valid_conditions = { :controller => true, :action => true }
- request_class.public_instance_methods.each { |m|
- @valid_conditions[m] = true
- }
- @valid_conditions.delete(:id)
@append = []
@prepend = []
@@ -375,7 +369,7 @@ module ActionDispatch
raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
path = build_path(conditions.delete(:path_info), requirements, SEPARATORS, anchor)
- conditions = build_conditions(conditions, valid_conditions, path.names.map { |x| x.to_sym })
+ conditions = build_conditions(conditions, path.names.map { |x| x.to_sym })
route = @set.add_route(app, path, conditions, defaults, name)
named_routes[name] = route if name && !named_routes[name]
@@ -412,21 +406,22 @@ module ActionDispatch
end
private :build_path
- def build_conditions(current_conditions, req_predicates, path_values)
+ def build_conditions(current_conditions, path_values)
conditions = current_conditions.dup
- verbs = conditions[:request_method] || []
-
# Rack-Mount requires that :request_method be a regular expression.
# :request_method represents the HTTP verb that matches this route.
#
# Here we munge values before they get sent on to rack-mount.
+ verbs = conditions[:request_method] || []
unless verbs.empty?
conditions[:request_method] = %r[^#{verbs.join('|')}$]
end
- conditions.delete_if { |k,v| !(req_predicates.include?(k) || path_values.include?(k)) }
- conditions
+ conditions.keep_if do |k, _|
+ k == :action || k == :controller ||
+ @request_class.public_method_defined?(k) || path_values.include?(k)
+ end
end
private :build_conditions
@@ -468,7 +463,7 @@ module ActionDispatch
def use_recall_for(key)
if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
if !named_route_exists? || segment_keys.include?(key)
- @options[key] = @recall.delete(key)
+ @options[key] = @recall.delete(key)
end
end
end
@@ -577,7 +572,8 @@ module ActionDispatch
end
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
- :trailing_slash, :anchor, :params, :only_path, :script_name]
+ :trailing_slash, :anchor, :params, :only_path, :script_name,
+ :original_script_name]
def mounted?
false
@@ -597,7 +593,13 @@ module ActionDispatch
user, password = extract_authentication(options)
recall = options.delete(:_recall)
- script_name = options.delete(:script_name).presence || _generate_prefix(options)
+
+ original_script_name = options.delete(:original_script_name).presence
+ script_name = options.delete(:script_name).presence || _generate_prefix(options)
+
+ if script_name && original_script_name
+ script_name = original_script_name + script_name
+ end
path_options = options.except(*RESERVED_OPTIONS)
path_options = yield(path_options) if block_given?
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 2b3095a234..ab584abf68 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -345,7 +345,7 @@ module ActionDispatch
define_method(method) do |*args|
reset! unless integration_session
# reset the html_document variable, but only for new get/post calls
- @html_document = nil unless method.in?(["cookies", "assigns"])
+ @html_document = nil unless method == 'cookies' || method == 'assigns'
integration_session.__send__(method, *args).tap do
copy_session_variables!
end
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 4bd72c5520..9d11c284f5 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -38,7 +38,6 @@ module ActionView
autoload :PathSet
autoload :Template
-
autoload_under "renderer" do
autoload :Renderer
autoload :AbstractRenderer
@@ -77,6 +76,11 @@ module ActionView
autoload :TestCase
ENCODING_FLAG = '#.*coding[:=]\s*(\S+)[ \t]*'
+
+ def self.eager_load!
+ super
+ ActionView::Template.eager_load!
+ end
end
require 'active_support/core_ext/string/output_safety'
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 68b0195700..ceb56824fa 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -209,18 +209,18 @@ module ActionView
# * <tt>:title</tt> - Specify the title of the link, defaults to the +type+
#
# ==== Examples
- # auto_discovery_link_tag # =>
- # <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
- # auto_discovery_link_tag(:atom) # =>
- # <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
- # auto_discovery_link_tag(:rss, {:action => "feed"}) # =>
- # <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
- # auto_discovery_link_tag(:rss, {:action => "feed"}, {:title => "My RSS"}) # =>
- # <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.currenthost.com/controller/feed" />
- # auto_discovery_link_tag(:rss, {:controller => "news", :action => "feed"}) # =>
- # <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/news/feed" />
- # auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {:title => "Example RSS"}) # =>
- # <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" />
+ # auto_discovery_link_tag
+ # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
+ # auto_discovery_link_tag(:atom)
+ # # => <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
+ # auto_discovery_link_tag(:rss, {:action => "feed"})
+ # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
+ # auto_discovery_link_tag(:rss, {:action => "feed"}, {:title => "My RSS"})
+ # # => <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.currenthost.com/controller/feed" />
+ # auto_discovery_link_tag(:rss, {:controller => "news", :action => "feed"})
+ # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/news/feed" />
+ # auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {:title => "Example RSS"})
+ # # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" />
def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
tag(
"link",
@@ -360,18 +360,18 @@ module ActionView
# width="30" and height="45". <tt>:size</tt> will be ignored if the
# value is not in the correct format.
#
- # image_tag("icon") # =>
- # <img src="/assets/icon" alt="Icon" />
- # image_tag("icon.png") # =>
- # <img src="/assets/icon.png" alt="Icon" />
- # image_tag("icon.png", :size => "16x10", :alt => "Edit Entry") # =>
- # <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" />
- # image_tag("/icons/icon.gif", :size => "16x16") # =>
- # <img src="/icons/icon.gif" width="16" height="16" alt="Icon" />
- # image_tag("/icons/icon.gif", :height => '32', :width => '32') # =>
- # <img alt="Icon" height="32" src="/icons/icon.gif" width="32" />
- # image_tag("/icons/icon.gif", :class => "menu_icon") # =>
- # <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
+ # image_tag("icon")
+ # # => <img src="/assets/icon" alt="Icon" />
+ # image_tag("icon.png")
+ # # => <img src="/assets/icon.png" alt="Icon" />
+ # image_tag("icon.png", :size => "16x10", :alt => "Edit Entry")
+ # # => <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" />
+ # image_tag("/icons/icon.gif", :size => "16x16")
+ # # => <img src="/icons/icon.gif" width="16" height="16" alt="Icon" />
+ # image_tag("/icons/icon.gif", :height => '32', :width => '32')
+ # # => <img alt="Icon" height="32" src="/icons/icon.gif" width="32" />
+ # image_tag("/icons/icon.gif", :class => "menu_icon")
+ # # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
def image_tag(source, options={})
options = options.symbolize_keys
@@ -408,24 +408,24 @@ module ActionView
# width="30" and height="45". <tt>:size</tt> will be ignored if the
# value is not in the correct format.
#
- # video_tag("trailer") # =>
- # <video src="/videos/trailer" />
- # video_tag("trailer.ogg") # =>
- # <video src="/videos/trailer.ogg" />
- # video_tag("trailer.ogg", :controls => true, :autobuffer => true) # =>
- # <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
- # video_tag("trailer.m4v", :size => "16x10", :poster => "screenshot.png") # =>
- # <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" />
- # video_tag("/trailers/hd.avi", :size => "16x16") # =>
- # <video src="/trailers/hd.avi" width="16" height="16" />
- # video_tag("/trailers/hd.avi", :height => '32', :width => '32') # =>
- # <video height="32" src="/trailers/hd.avi" width="32" />
- # video_tag("trailer.ogg", "trailer.flv") # =>
- # <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
- # video_tag(["trailer.ogg", "trailer.flv"]) # =>
- # <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
- # video_tag(["trailer.ogg", "trailer.flv"], :size => "160x120") # =>
- # <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
+ # video_tag("trailer")
+ # # => <video src="/videos/trailer" />
+ # video_tag("trailer.ogg")
+ # # => <video src="/videos/trailer.ogg" />
+ # video_tag("trailer.ogg", :controls => true, :autobuffer => true)
+ # # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
+ # video_tag("trailer.m4v", :size => "16x10", :poster => "screenshot.png")
+ # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" />
+ # video_tag("/trailers/hd.avi", :size => "16x16")
+ # # => <video src="/trailers/hd.avi" width="16" height="16" />
+ # video_tag("/trailers/hd.avi", :height => '32', :width => '32')
+ # # => <video height="32" src="/trailers/hd.avi" width="32" />
+ # video_tag("trailer.ogg", "trailer.flv")
+ # # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
+ # video_tag(["trailer.ogg", "trailer.flv"])
+ # # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
+ # video_tag(["trailer.ogg", "trailer.flv"], :size => "160x120")
+ # # => <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
def video_tag(*sources)
multiple_sources_tag('video', sources) do |options|
options[:poster] = path_to_image(options[:poster]) if options[:poster]
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
index 14d62af67b..139f4d19ab 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
@@ -26,7 +26,8 @@ module ActionView
def expand_sources(sources, recursive = false)
if sources.include?(:all)
- all_asset_files = (collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}") - ['application']) << 'application'
+ all_asset_files = (collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}") - ['application'])
+ add_application_js(all_asset_files, sources)
((determine_source(:defaults, expansions).dup & all_asset_files) + all_asset_files).uniq
else
expanded_sources = sources.inject([]) do |list, source|
@@ -39,7 +40,7 @@ module ActionView
end
def add_application_js(expanded_sources, sources)
- if sources.include?(:defaults) && File.exist?(File.join(custom_dir, "application.#{extension}"))
+ if (sources.include?(:defaults) || sources.include?(:all)) && File.exist?(File.join(custom_dir, "application.#{extension}"))
expanded_sources.delete('application')
expanded_sources << "application"
end
@@ -106,8 +107,8 @@ module ActionView
#
# config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js)
#
- # When using <tt>:defaults</tt>, if an <tt>application.js</tt> file exists in
- # <tt>public/javascripts</tt> it will be included as well at the end.
+ # When using <tt>:defaults</tt> or <tt>:all</tt>, if an <tt>application.js</tt> file exists
+ # in <tt>public/javascripts</tt> it will be included as well at the end.
#
# You can modify the HTML attributes of the script tag by passing a hash as the
# last argument.
@@ -133,14 +134,16 @@ module ActionView
# # <script src="/javascripts/rails.js?1284139606"></script>
# # <script src="/javascripts/application.js?1284139606"></script>
#
+ # Note: The application.js file is only referenced if it exists
+ #
# You can also include all JavaScripts in the +javascripts+ directory using <tt>:all</tt> as the source:
#
# javascript_include_tag :all
# # => <script src="/javascripts/jquery.js?1284139606"></script>
# # <script src="/javascripts/rails.js?1284139606"></script>
- # # <script src="/javascripts/application.js?1284139606"></script>
# # <script src="/javascripts/shop.js?1284139606"></script>
# # <script src="/javascripts/checkout.js?1284139606"></script>
+ # # <script src="/javascripts/application.js?1284139606"></script>
#
# Note that your defaults of choice will be included first, so they will be available to all subsequently
# included files.
@@ -161,9 +164,9 @@ module ActionView
# javascript_include_tag :all, :cache => true
# # => <script src="/javascripts/jquery.js?1284139606"></script>
# # <script src="/javascripts/rails.js?1284139606"></script>
- # # <script src="/javascripts/application.js?1284139606"></script>
# # <script src="/javascripts/shop.js?1284139606"></script>
# # <script src="/javascripts/checkout.js?1284139606"></script>
+ # # <script src="/javascripts/application.js?1284139606"></script>
#
# # assuming config.perform_caching is true
# javascript_include_tag :all, :cache => true
diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb
index 33799d7d71..39518268df 100644
--- a/actionpack/lib/action_view/helpers/cache_helper.rb
+++ b/actionpack/lib/action_view/helpers/cache_helper.rb
@@ -8,28 +8,28 @@ module ActionView
# fragments, and so on. This method takes a block that contains
# the content you wish to cache.
#
- # See ActionController::Caching::Fragments for usage instructions.
+ # The best way to use this is by doing key-based cache expiration
+ # on top of a cache store like Memcached that'll automatically
+ # kick out old entries. For more on key-based expiration, see:
+ # http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works
#
- # If you want to cache a navigation menu, you can do following:
+ # When using this method, you list the cache dependencies as part of
+ # the name of the cache, like so:
#
- # <% cache do %>
- # <%= render :partial => "menu" %>
+ # <% cache [ "v1", project ] do %>
+ # <b>All the topics on this project</b>
+ # <%= render project.topics %>
# <% end %>
#
- # You can also cache static content:
+ # This approach will assume that when a new topic is added, you'll touch
+ # the project. The cache key generated from this call will be something like:
#
- # <% cache do %>
- # <p>Hello users! Welcome to our website!</p>
- # <% end %>
- #
- # Static content with embedded ruby content can be cached as
- # well:
+ # views/v1/projects/123-20120806214154
+ # ^class ^id ^updated_at
#
- # <% cache do %>
- # Topics:
- # <%= render :partial => "topics", :collection => @topic_list %>
- # <i>Topics listed alphabetically</i>
- # <% end %>
+ # If you update the rendering of topics, you just bump the version to v2.
+ # Otherwise the cache is automatically bumped whenever the project updated_at
+ # is touched.
def cache(name = {}, options = nil, &block)
if controller.perform_caching
safe_concat(fragment_for(name, options, &block))
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 659aacf6d7..795440cacc 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -140,12 +140,19 @@ module ActionView
# Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
#
# time_ago_in_words(3.minutes.from_now) # => 3 minutes
+ # time_ago_in_words(3.minutes.ago) # => 3 minutes
# time_ago_in_words(Time.now - 15.hours) # => about 15 hours
# time_ago_in_words(Time.now) # => less than a minute
# time_ago_in_words(Time.now, :include_seconds => true) # => less than 5 seconds
#
# from_time = Time.now - 3.days - 14.minutes - 25.seconds
# time_ago_in_words(from_time) # => 3 days
+ #
+ # from_time = (3.days + 14.minutes + 25.seconds).ago
+ # time_ago_in_words(from_time) # => 3 days
+ #
+ # Note that you cannot pass a <tt>Numeric</tt> value to <tt>time_ago_in_words</tt>.
+ #
def time_ago_in_words(from_time, include_seconds_or_options = {})
distance_of_time_in_words(from_time, Time.now, include_seconds_or_options)
end
@@ -427,6 +434,9 @@ module ActionView
# # Generate a time select field with hours in the AM/PM format
# select_time(my_time, :ampm => true)
#
+ # # Generates a time select field with hours that range from 2 to 14
+ # select_time(my_time, :start_hour => 2, :end_hour => 14)
+ #
# # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts.
# select_time(my_time, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
# select_time(my_time, :prompt => {:hour => true}) # generic prompt for hours
@@ -504,6 +514,9 @@ module ActionView
#
# # Generate a select field for hours in the AM/PM format
# select_hour(my_time, :ampm => true)
+ #
+ # # Generates a select field that includes options for hours from 2 to 14.
+ # select_hour(my_time, :start_hour => 2, :end_hour => 14)
def select_hour(datetime, options = {}, html_options = {})
DateTimeSelector.new(datetime, options, html_options).select_hour
end
@@ -734,7 +747,11 @@ module ActionView
if @options[:use_hidden] || @options[:discard_hour]
build_hidden(:hour, hour)
else
- build_options_and_select(:hour, hour, :end => 23, :ampm => @options[:ampm])
+ options = {}
+ options[:ampm] = @options[:ampm] || false
+ options[:start] = @options[:start_hour] || 0
+ options[:end] = @options[:end_hour] || 23
+ build_options_and_select(:hour, hour, options)
end
end
@@ -946,12 +963,15 @@ module ActionView
# build_hidden(:year, 2008)
# => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
def build_hidden(type, value)
- (tag(:input, {
+ select_options = {
:type => "hidden",
:id => input_id_from_type(type),
:name => input_name_from_type(type),
:value => value
- }.merge(@html_options.slice(:disabled))) + "\n").html_safe
+ }.merge(@html_options.slice(:disabled))
+ select_options.merge!(:disabled => 'disabled') if @options[:disabled]
+
+ tag(:input, select_options) + "\n".html_safe
end
# Returns the name attribute for the input tag.
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 13a5671a17..b79577bcd3 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -323,6 +323,24 @@ module ActionView
# ...
# </form>
#
+ # === Setting HTML options
+ #
+ # You can set data attributes directly by passing in a data hash, but all other HTML options must be wrapped in
+ # the HTML key. Example:
+ #
+ # <%= form_for(@post, data: { behavior: "autosave" }, html: { name: "go" }) do |f| %>
+ # ...
+ # <% end %>
+ #
+ # The HTML generated for this would be:
+ #
+ # <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'>
+ # <div style='margin:0;padding:0;display:inline'>
+ # <input name='_method' type='hidden' value='put' />
+ # </div>
+ # ...
+ # </form>
+ #
# === Removing hidden model id's
#
# The form_for method automatically includes the model id as a hidden field in the form.
@@ -405,10 +423,12 @@ module ActionView
object = nil
else
object = record.is_a?(Array) ? record.last : record
+ raise ArgumentError, "First argument in form cannot contain nil or be empty" if object.blank?
object_name = options[:as] || model_name_from_record_or_class(object).param_key
apply_form_for_options!(record, object, options)
end
+ options[:html][:data] = options.delete(:data) if options.has_key?(:data)
options[:html][:remote] = options.delete(:remote) if options.has_key?(:remote)
options[:html][:method] = options.delete(:method) if options.has_key?(:method)
options[:html][:authenticity_token] = options.delete(:authenticity_token)
@@ -700,15 +720,15 @@ module ActionView
# label(:post, :title)
# # => <label for="post_title">Title</label>
#
- # You can localize your labels based on model and attribute names.
- # For example you can define the following in your locale (e.g. en.yml)
+ # You can localize your labels based on model and attribute names.
+ # For example you can define the following in your locale (e.g. en.yml)
#
# helpers:
# label:
# post:
# body: "Write your entire text here"
#
- # Which then will result in
+ # Which then will result in
#
# label(:post, :body)
# # => <label for="post_body">Write your entire text here</label>
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index f3237f82d5..2bb526a539 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -501,14 +501,14 @@ module ActionView
#
# Possible output:
# <optgroup label="---------">
+ # <option value="US">United States</option>
+ # <option value="Canada">Canada</option>
+ # </optgroup>
+ # <optgroup label="---------">
# <option value="Denmark">Denmark</option>
# <option value="Germany">Germany</option>
# <option value="France">France</option>
# </optgroup>
- # <optgroup label="---------">
- # <option value="US">United States</option>
- # <option value="Canada">Canada</option>
- # </optgroup>
#
# <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
# wrap the output in an appropriate <tt><select></tt> tag.
@@ -708,9 +708,11 @@ module ActionView
private
def option_html_attributes(element)
- return {} unless Array === element
-
- Hash[element.select { |e| Hash === e }.reduce({}, :merge).map { |k, v| [k, v] }]
+ if Array === element
+ element.select { |e| Hash === e }.reduce({}, :merge!)
+ else
+ {}
+ end
end
def option_text_and_value(option)
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 46ee196a2a..ace457df2e 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -6,7 +6,7 @@ require 'active_support/core_ext/module/attribute_accessors'
module ActionView
# = Action View Form Tag Helpers
module Helpers
- # Provides a number of methods for creating form tags that doesn't rely on an Active Record object assigned to the template like
+ # Provides a number of methods for creating form tags that don't rely on an Active Record object assigned to the template like
# FormHelper does. Instead, you provide the names and values manually.
#
# NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
@@ -118,6 +118,7 @@ module ActionView
# # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
# # <option>Paris</option><option>Rome</option></select>
def select_tag(name, option_tags = nil, options = {})
+ option_tags ||= ""
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
if options.delete(:include_blank)
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index cc20518b93..9f8cd8caaa 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -67,6 +67,46 @@ module ActionView
def javascript_cdata_section(content) #:nodoc:
"\n//#{cdata_section("\n#{content}\n//")}\n".html_safe
end
+
+ # Returns a button whose +onclick+ handler triggers the passed JavaScript.
+ #
+ # The helper receives a name, JavaScript code, and an optional hash of HTML options. The
+ # name is used as button label and the JavaScript code goes into its +onclick+ attribute.
+ # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+.
+ #
+ # button_to_function "Greeting", "alert('Hello world!')", :class => "ok"
+ # # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" />
+ #
+ def button_to_function(name, function=nil, html_options={})
+ message = "button_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead."
+ ActiveSupport::Deprecation.warn message
+
+ onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
+
+ tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
+ end
+
+ # Returns a link whose +onclick+ handler triggers the passed JavaScript.
+ #
+ # The helper receives a name, JavaScript code, and an optional hash of HTML options. The
+ # name is used as the link text and the JavaScript code goes into the +onclick+ attribute.
+ # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+. Once all
+ # the JavaScript is set, the helper appends "; return false;".
+ #
+ # The +href+ attribute of the tag is set to "#" unless +html_options+ has one.
+ #
+ # link_to_function "Greeting", "alert('Hello world!')", :class => "nav_link"
+ # # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a>
+ #
+ def link_to_function(name, function, html_options={})
+ message = "link_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead."
+ ActiveSupport::Deprecation.warn message
+
+ onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
+ href = html_options[:href] || '#'
+
+ content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
+ end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb
index a727b910e5..aaf0e0344a 100644
--- a/actionpack/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -78,7 +78,7 @@ module ActionView
# strip_tags("<div id='top-bar'>Welcome to my website!</div>")
# # => Welcome to my website!
def strip_tags(html)
- self.class.full_sanitizer.sanitize(html).try(:html_safe)
+ self.class.full_sanitizer.sanitize(html)
end
# Strips all link tags from +text+ leaving just the link text.
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 3d86790a8f..fe3240fdc1 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -60,11 +60,15 @@ module ActionView
#
# ==== Relying on named routes
#
- # Passing a record (like an Active Record) instead of a Hash as the options parameter will
+ # Passing a record (like an Active Record) instead of a hash as the options parameter will
# trigger the named route for that record. The lookup will happen on the name of the class. So passing a
# Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
# +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
#
+ # ==== Implicit Controller Namespacing
+ #
+ # Controllers passed in using the +:controller+ option will retain their namespace unless it is an absolute one.
+ #
# ==== Examples
# <%= url_for(:action => 'index') %>
# # => /blog/
@@ -102,6 +106,14 @@ module ActionView
# <%= url_for(:back) %>
# # if request.env["HTTP_REFERER"] is not set or is blank
# # => javascript:history.back()
+ #
+ # <%= url_for(:action => 'index', :controller => 'users') %>
+ # # Assuming an "admin" namespace
+ # # => /admin/users
+ #
+ # <%= url_for(:action => 'index', :controller => '/users') %>
+ # # Specify absolute path with beginning slash
+ # # => /users
def url_for(options = nil)
case options
when String
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index 9f5e3be454..2d36deaa78 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -9,6 +9,8 @@ module ActionView
config.action_view.javascript_expansions = { :defaults => %w(jquery jquery_ujs) }
config.action_view.embed_authenticity_token_in_remote_forms = false
+ config.eager_load_namespaces << ActionView
+
initializer "action_view.embed_authenticity_token_in_remote_forms" do |app|
ActiveSupport.on_load(:action_view) do
ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms =
diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb
index d8258f7b11..f2bef4bded 100644
--- a/actionpack/lib/action_view/template/error.rb
+++ b/actionpack/lib/action_view/template/error.rb
@@ -84,13 +84,13 @@ module ActionView
start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
- indent = ' ' * indentation
+ indent = end_on_line.to_s.size + indentation
line_counter = start_on_line
return unless source_code = source_code[start_on_line..end_on_line]
source_code.sum do |line|
line_counter += 1
- "#{indent}#{line_counter}: #{line}\n"
+ "%#{indent}s: %s\n" % [line_counter, line]
end
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 56cebee678..e5054a9eb8 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -85,39 +85,28 @@ module RenderERBUtils
end
end
-module SetupOnce
- extend ActiveSupport::Concern
-
- included do
- cattr_accessor :setup_once_block
- self.setup_once_block = nil
-
- setup :run_setup_once
- end
+SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
- module ClassMethods
- def setup_once(&block)
- self.setup_once_block = block
+module ActionDispatch
+ module SharedRoutes
+ def before_setup
+ @routes = SharedTestRoutes
+ super
end
end
- private
- def run_setup_once
- if self.setup_once_block
- self.setup_once_block.call
- self.setup_once_block = nil
- end
+ # Hold off drawing routes until all the possible controller classes
+ # have been loaded.
+ module DrawOnce
+ class << self
+ attr_accessor :drew
end
-end
+ self.drew = false
-SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
+ def before_setup
+ super
+ return if DrawOnce.drew
-module ActiveSupport
- class TestCase
- include SetupOnce
- # Hold off drawing routes until all the possible controller classes
- # have been loaded.
- setup_once do
SharedTestRoutes.draw do
get ':controller(/:action)'
end
@@ -125,10 +114,18 @@ module ActiveSupport
ActionDispatch::IntegrationTest.app.routes.draw do
get ':controller(/:action)'
end
+
+ DrawOnce.drew = true
end
end
end
+module ActiveSupport
+ class TestCase
+ include ActionDispatch::DrawOnce
+ end
+end
+
class RoutedRackApp
attr_reader :routes
@@ -159,9 +156,7 @@ class BasicController
end
class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
- setup do
- @routes = SharedTestRoutes
- end
+ include ActionDispatch::SharedRoutes
def self.build_app(routes = nil)
RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
@@ -290,10 +285,7 @@ module ActionController
class TestCase
include ActionDispatch::TestProcess
-
- setup do
- @routes = SharedTestRoutes
- end
+ include ActionDispatch::SharedRoutes
end
end
@@ -304,9 +296,7 @@ module ActionView
class TestCase
# Must repeat the setup because AV::TestCase is a duplication
# of AC::TestCase
- setup do
- @routes = SharedTestRoutes
- end
+ include ActionDispatch::SharedRoutes
end
end
@@ -359,3 +349,32 @@ module RoutingTestHelpers
set.send(:url_for, options.merge(:only_path => true, :_recall => recall))
end
end
+
+class ResourcesController < ActionController::Base
+ def index() render :nothing => true end
+ alias_method :show, :index
+end
+
+class ThreadsController < ResourcesController; end
+class MessagesController < ResourcesController; end
+class CommentsController < ResourcesController; end
+class AuthorsController < ResourcesController; end
+class LogosController < ResourcesController; end
+
+class AccountsController < ResourcesController; end
+class AdminController < ResourcesController; end
+class ProductsController < ResourcesController; end
+class ImagesController < ResourcesController; end
+class PreferencesController < ResourcesController; end
+
+module Backoffice
+ class ProductsController < ResourcesController; end
+ class TagsController < ResourcesController; end
+ class ManufacturersController < ResourcesController; end
+ class ImagesController < ResourcesController; end
+
+ module Admin
+ class ProductsController < ResourcesController; end
+ class ImagesController < ResourcesController; end
+ end
+end
diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb
deleted file mode 100644
index 275f25f597..0000000000
--- a/actionpack/test/activerecord/active_record_store_test.rb
+++ /dev/null
@@ -1,288 +0,0 @@
-require 'active_record_unit'
-
-class ActiveRecordStoreTest < ActionDispatch::IntegrationTest
- class TestController < ActionController::Base
- def no_session_access
- head :ok
- end
-
- def set_session_value
- raise "missing session!" unless session
- session[:foo] = params[:foo] || "bar"
- head :ok
- end
-
- def get_session_value
- render :text => "foo: #{session[:foo].inspect}"
- end
-
- def get_session_id
- render :text => "#{request.session_options[:id]}"
- end
-
- def call_reset_session
- session[:foo]
- reset_session
- reset_session if params[:twice]
- session[:foo] = "baz"
- head :ok
- end
-
- def renew
- env["rack.session.options"][:renew] = true
- session[:foo] = "baz"
- head :ok
- end
- end
-
- def setup
- ActiveRecord::SessionStore.session_class.create_table!
- end
-
- def teardown
- ActiveRecord::SessionStore.session_class.drop_table!
- end
-
- %w{ session sql_bypass }.each do |class_name|
- define_method("test_setting_and_getting_session_value_with_#{class_name}_store") do
- with_store class_name do
- with_test_route_set do
- get '/set_session_value'
- assert_response :success
- assert cookies['_session_id']
-
- get '/get_session_value'
- assert_response :success
- assert_equal 'foo: "bar"', response.body
-
- get '/set_session_value', :foo => "baz"
- assert_response :success
- assert cookies['_session_id']
-
- get '/get_session_value'
- assert_response :success
- assert_equal 'foo: "baz"', response.body
-
- get '/call_reset_session'
- assert_response :success
- assert_not_equal [], headers['Set-Cookie']
- end
- end
- end
-
- define_method("test_renewing_with_#{class_name}_store") do
- with_store class_name do
- with_test_route_set do
- get '/set_session_value'
- assert_response :success
- assert cookies['_session_id']
-
- get '/renew'
- assert_response :success
- assert_not_equal [], headers['Set-Cookie']
- end
- end
- end
- end
-
- def test_getting_nil_session_value
- with_test_route_set do
- get '/get_session_value'
- assert_response :success
- assert_equal 'foo: nil', response.body
- end
- end
-
- def test_calling_reset_session_twice_does_not_raise_errors
- with_test_route_set do
- get '/call_reset_session', :twice => "true"
- assert_response :success
-
- get '/get_session_value'
- assert_response :success
- assert_equal 'foo: "baz"', response.body
- end
- end
-
- def test_setting_session_value_after_session_reset
- with_test_route_set do
- get '/set_session_value'
- assert_response :success
- assert cookies['_session_id']
- session_id = cookies['_session_id']
-
- get '/call_reset_session'
- assert_response :success
- assert_not_equal [], headers['Set-Cookie']
-
- get '/get_session_value'
- assert_response :success
- assert_equal 'foo: "baz"', response.body
-
- get '/get_session_id'
- assert_response :success
- assert_not_equal session_id, response.body
- end
- end
-
- def test_getting_session_value_after_session_reset
- with_test_route_set do
- get '/set_session_value'
- assert_response :success
- assert cookies['_session_id']
- session_cookie = cookies.send(:hash_for)['_session_id']
-
- get '/call_reset_session'
- assert_response :success
- assert_not_equal [], headers['Set-Cookie']
-
- cookies << session_cookie # replace our new session_id with our old, pre-reset session_id
-
- get '/get_session_value'
- assert_response :success
- assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from the database"
- end
- end
-
- def test_getting_from_nonexistent_session
- with_test_route_set do
- get '/get_session_value'
- assert_response :success
- assert_equal 'foo: nil', response.body
- assert_nil cookies['_session_id'], "should only create session on write, not read"
- end
- end
-
- def test_getting_session_id
- with_test_route_set do
- get '/set_session_value'
- assert_response :success
- assert cookies['_session_id']
- session_id = cookies['_session_id']
-
- get '/get_session_id'
- assert_response :success
- assert_equal session_id, response.body, "should be able to read session id without accessing the session hash"
- end
- end
-
- def test_doesnt_write_session_cookie_if_session_id_is_already_exists
- with_test_route_set do
- get '/set_session_value'
- assert_response :success
- assert cookies['_session_id']
-
- get '/get_session_value'
- assert_response :success
- assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists"
- end
- end
-
- def test_prevents_session_fixation
- with_test_route_set do
- get '/set_session_value'
- assert_response :success
- assert cookies['_session_id']
-
- get '/get_session_value'
- assert_response :success
- assert_equal 'foo: "bar"', response.body
- session_id = cookies['_session_id']
- assert session_id
-
- reset!
-
- get '/get_session_value', :_session_id => session_id
- assert_response :success
- assert_equal 'foo: nil', response.body
- assert_not_equal session_id, cookies['_session_id']
- end
- end
-
- def test_allows_session_fixation
- with_test_route_set(:cookie_only => false) do
- get '/set_session_value'
- assert_response :success
- assert cookies['_session_id']
-
- get '/get_session_value'
- assert_response :success
- assert_equal 'foo: "bar"', response.body
- session_id = cookies['_session_id']
- assert session_id
-
- reset!
-
- get '/set_session_value', :_session_id => session_id, :foo => "baz"
- assert_response :success
- assert_equal session_id, cookies['_session_id']
-
- get '/get_session_value', :_session_id => session_id
- assert_response :success
- assert_equal 'foo: "baz"', response.body
- assert_equal session_id, cookies['_session_id']
- end
- end
-
- def test_incoming_invalid_session_id_via_cookie_should_be_ignored
- with_test_route_set do
- open_session do |sess|
- sess.cookies['_session_id'] = 'INVALID'
-
- sess.get '/set_session_value'
- new_session_id = sess.cookies['_session_id']
- assert_not_equal 'INVALID', new_session_id
-
- sess.get '/get_session_value'
- new_session_id_2 = sess.cookies['_session_id']
- assert_equal new_session_id, new_session_id_2
- end
- end
- end
-
- def test_incoming_invalid_session_id_via_parameter_should_be_ignored
- with_test_route_set(:cookie_only => false) do
- open_session do |sess|
- sess.get '/set_session_value', :_session_id => 'INVALID'
- new_session_id = sess.cookies['_session_id']
- assert_not_equal 'INVALID', new_session_id
-
- sess.get '/get_session_value'
- new_session_id_2 = sess.cookies['_session_id']
- assert_equal new_session_id, new_session_id_2
- end
- end
- end
-
- def test_session_store_with_all_domains
- with_test_route_set(:domain => :all) do
- get '/set_session_value'
- assert_response :success
- end
- end
-
- private
-
- def with_test_route_set(options = {})
- with_routing do |set|
- set.draw do
- get ':action', :to => 'active_record_store_test/test'
- end
-
- @app = self.class.build_app(set) do |middleware|
- middleware.use ActiveRecord::SessionStore, options.reverse_merge(:key => '_session_id')
- middleware.delete "ActionDispatch::ShowExceptions"
- end
-
- yield
- end
- end
-
- def with_store(class_name)
- session_class, ActiveRecord::SessionStore.session_class =
- ActiveRecord::SessionStore.session_class, "ActiveRecord::SessionStore::#{class_name.camelize}".constantize
- yield
- ensure
- ActiveRecord::SessionStore.session_class = session_class
- end
-end
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index d5afef9086..0efba5b77f 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -349,15 +349,18 @@ class ActionCachingMockController
end
class ActionCacheTest < ActionController::TestCase
+ tests ActionCachingTestController
+
def setup
super
- reset!
+ @request.host = 'hostname.com'
FileUtils.mkdir_p(FILE_STORE_PATH)
@path_class = ActionController::Caching::Actions::ActionCachePath
@mock_controller = ActionCachingMockController.new
end
def teardown
+ super
FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
end
@@ -367,7 +370,6 @@ class ActionCacheTest < ActionController::TestCase
cached_time = content_to_cache
assert_equal cached_time, @response.body
assert fragment_exist?('hostname.com/action_caching_test')
- reset!
get :index
assert_response :success
@@ -380,7 +382,6 @@ class ActionCacheTest < ActionController::TestCase
cached_time = content_to_cache
assert_equal cached_time, @response.body
assert !fragment_exist?('hostname.com/action_caching_test/destroy')
- reset!
get :destroy
assert_response :success
@@ -395,7 +396,6 @@ class ActionCacheTest < ActionController::TestCase
cached_time = content_to_cache
assert_not_equal cached_time, @response.body
assert fragment_exist?('hostname.com/action_caching_test/with_layout')
- reset!
get :with_layout
assert_response :success
@@ -410,7 +410,6 @@ class ActionCacheTest < ActionController::TestCase
cached_time = content_to_cache
assert_not_equal cached_time, @response.body
assert fragment_exist?('hostname.com/action_caching_test/layout_false')
- reset!
get :layout_false
assert_response :success
@@ -425,7 +424,6 @@ class ActionCacheTest < ActionController::TestCase
cached_time = content_to_cache
assert_not_equal cached_time, @response.body
assert fragment_exist?('hostname.com/action_caching_test/with_layout_proc_param')
- reset!
get :with_layout_proc_param, :layout => false
assert_response :success
@@ -440,7 +438,6 @@ class ActionCacheTest < ActionController::TestCase
cached_time = content_to_cache
assert_not_equal cached_time, @response.body
assert fragment_exist?('hostname.com/action_caching_test/with_layout_proc_param')
- reset!
get :with_layout_proc_param, :layout => true
assert_response :success
@@ -477,7 +474,6 @@ class ActionCacheTest < ActionController::TestCase
cached_time = content_to_cache
assert_equal cached_time, @response.body
assert fragment_exist?('test.host/custom/show')
- reset!
get :show
assert_response :success
@@ -488,7 +484,6 @@ class ActionCacheTest < ActionController::TestCase
get :edit
assert_response :success
assert fragment_exist?('test.host/edit')
- reset!
get :edit, :id => 1
assert_response :success
@@ -499,22 +494,18 @@ class ActionCacheTest < ActionController::TestCase
get :index
assert_response :success
cached_time = content_to_cache
- reset!
get :index
assert_response :success
assert_equal cached_time, @response.body
- reset!
get :expire
assert_response :success
- reset!
get :index
assert_response :success
new_cached_time = content_to_cache
assert_not_equal cached_time, @response.body
- reset!
get :index
assert_response :success
@@ -524,12 +515,10 @@ class ActionCacheTest < ActionController::TestCase
def test_cache_expiration_isnt_affected_by_request_format
get :index
cached_time = content_to_cache
- reset!
@request.request_uri = "/action_caching_test/expire.xml"
get :expire, :format => :xml
assert_response :success
- reset!
get :index
assert_response :success
@@ -539,12 +528,10 @@ class ActionCacheTest < ActionController::TestCase
def test_cache_expiration_with_url_string
get :index
cached_time = content_to_cache
- reset!
@request.request_uri = "/action_caching_test/expire_with_url_string"
get :expire_with_url_string
assert_response :success
- reset!
get :index
assert_response :success
@@ -557,23 +544,17 @@ class ActionCacheTest < ActionController::TestCase
assert_response :success
jamis_cache = content_to_cache
- reset!
-
@request.host = 'david.hostname.com'
get :index
assert_response :success
david_cache = content_to_cache
assert_not_equal jamis_cache, @response.body
- reset!
-
@request.host = 'jamis.hostname.com'
get :index
assert_response :success
assert_equal jamis_cache, @response.body
- reset!
-
@request.host = 'david.hostname.com'
get :index
assert_response :success
@@ -583,8 +564,6 @@ class ActionCacheTest < ActionController::TestCase
def test_redirect_is_not_cached
get :redirected
assert_response :redirect
- reset!
-
get :redirected
assert_response :redirect
end
@@ -592,8 +571,6 @@ class ActionCacheTest < ActionController::TestCase
def test_forbidden_is_not_cached
get :forbidden
assert_response :forbidden
- reset!
-
get :forbidden
assert_response :forbidden
end
@@ -609,17 +586,14 @@ class ActionCacheTest < ActionController::TestCase
cached_time = content_to_cache
assert_equal cached_time, @response.body
assert fragment_exist?('hostname.com/action_caching_test/index.xml')
- reset!
get :index, :format => 'xml'
assert_response :success
assert_equal cached_time, @response.body
assert_equal 'application/xml', @response.content_type
- reset!
get :expire_xml
assert_response :success
- reset!
get :index, :format => 'xml'
assert_response :success
@@ -724,14 +698,6 @@ class ActionCacheTest < ActionController::TestCase
assigns(:cache_this)
end
- def reset!
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- @controller = ActionCachingTestController.new
- @controller.singleton_class.send(:include, @routes.url_helpers)
- @request.host = 'hostname.com'
- end
-
def fragment_exist?(path)
@controller.fragment_exist?(path)
end
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index 6f9dd44e21..b21d35c846 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -151,10 +151,11 @@ class RespondToController < ActionController::Base
protected
def set_layout
- if action_name.in?(["all_types_with_layout", "iphone_with_html_response_type"])
- "respond_to/layouts/standard"
- elsif action_name == "iphone_with_html_response_type_without_layout"
- "respond_to/layouts/missing"
+ case action_name
+ when "all_types_with_layout", "iphone_with_html_response_type"
+ "respond_to/layouts/standard"
+ when "iphone_with_html_response_type_without_layout"
+ "respond_to/layouts/missing"
end
end
end
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index c7c367f18a..305659b219 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -2,35 +2,6 @@ require 'abstract_unit'
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/with_options'
-class ResourcesController < ActionController::Base
- def index() render :nothing => true end
- alias_method :show, :index
-end
-
-class ThreadsController < ResourcesController; end
-class MessagesController < ResourcesController; end
-class CommentsController < ResourcesController; end
-class AuthorsController < ResourcesController; end
-class LogosController < ResourcesController; end
-
-class AccountsController < ResourcesController; end
-class AdminController < ResourcesController; end
-class ProductsController < ResourcesController; end
-class ImagesController < ResourcesController; end
-class PreferencesController < ResourcesController; end
-
-module Backoffice
- class ProductsController < ResourcesController; end
- class TagsController < ResourcesController; end
- class ManufacturersController < ResourcesController; end
- class ImagesController < ResourcesController; end
-
- module Admin
- class ProductsController < ResourcesController; end
- class ImagesController < ResourcesController; end
- end
-end
-
class ResourcesTest < ActionController::TestCase
def test_default_restful_routes
with_restful_routing :messages do
@@ -1307,7 +1278,7 @@ class ResourcesTest < ActionController::TestCase
def assert_resource_methods(expected, resource, action_method, method)
assert_equal expected.length, resource.send("#{action_method}_methods")[method].size, "#{resource.send("#{action_method}_methods")[method].inspect}"
expected.each do |action|
- assert action.in?(resource.send("#{action_method}_methods")[method])
+ assert resource.send("#{action_method}_methods")[method].include?(action)
"#{method} not in #{action_method} methods: #{resource.send("#{action_method}_methods")[method].inspect}"
end
end
@@ -1344,9 +1315,9 @@ class ResourcesTest < ActionController::TestCase
options = options.merge(:action => action.to_s)
path_options = { :path => path, :method => method }
- if action.in?(Array(allowed))
+ if Array(allowed).include?(action)
assert_recognizes options, path_options
- elsif action.in?(Array(not_allowed))
+ elsif Array(not_allowed).include?(action)
assert_not_recognizes options, path_options
end
end
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 6cc1370105..57ab325683 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -197,7 +197,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
end
def test_regexp_precidence
- @rs.draw do
+ rs.draw do
get '/whois/:domain', :constraints => {
:domain => /\w+\.[\w\.]+/ },
:to => lambda { |env| [200, {}, %w{regexp}] }
@@ -216,7 +216,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
end
}
- @rs.draw do
+ rs.draw do
get '/', :constraints => subdomain.new,
:to => lambda { |env| [200, {}, %w{default}] }
get '/', :constraints => { :subdomain => 'clients' },
@@ -228,7 +228,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
end
def test_lambda_constraints
- @rs.draw do
+ rs.draw do
get '/', :constraints => lambda { |req|
req.subdomain.present? and req.subdomain != "clients" },
:to => lambda { |env| [200, {}, %w{default}] }
@@ -266,22 +266,22 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
def test_draw_with_block_arity_one_raises
assert_raise(RuntimeError) do
- @rs.draw { |map| map.match '/:controller(/:action(/:id))' }
+ rs.draw { |map| map.match '/:controller(/:action(/:id))' }
end
end
def test_specific_controller_action_failure
- @rs.draw do
+ rs.draw do
mount lambda {} => "/foo"
end
assert_raises(ActionController::RoutingError) do
- url_for(@rs, :controller => "omg", :action => "lol")
+ url_for(rs, :controller => "omg", :action => "lol")
end
end
def test_default_setup
- @rs.draw { get '/:controller(/:action(/:id))' }
+ rs.draw { get '/:controller(/:action(/:id))' }
assert_equal({:controller => "content", :action => 'index'}, rs.recognize_path("/content"))
assert_equal({:controller => "content", :action => 'list'}, rs.recognize_path("/content/list"))
assert_equal({:controller => "content", :action => 'show', :id => '10'}, rs.recognize_path("/content/show/10"))
@@ -298,8 +298,8 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
end
def test_ignores_leading_slash
- @rs.clear!
- @rs.draw { get '/:controller(/:action(/:id))'}
+ rs.clear!
+ rs.draw { get '/:controller(/:action(/:id))'}
test_default_setup
end
@@ -470,7 +470,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
end
def test_changing_controller
- @rs.draw { get ':controller/:action/:id' }
+ rs.draw { get ':controller/:action/:id' }
assert_equal '/admin/stuff/show/10',
url_for(rs, {:controller => 'stuff', :action => 'show', :id => 10},
@@ -583,7 +583,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
end
def test_action_expiry
- @rs.draw { get ':controller(/:action(/:id))' }
+ rs.draw { get ':controller(/:action(/:id))' }
assert_equal '/content', url_for(rs, { :controller => 'content' }, { :controller => 'content', :action => 'show' })
end
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index 2467654a70..347b3b3b5a 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -190,7 +190,7 @@ class CookiesTest < ActionController::TestCase
def test_setting_the_same_value_to_permanent_cookie
request.cookies[:user_name] = 'Jamie'
get :set_permanent_cookie
- assert response.cookies, 'user_name' => 'Jamie'
+ assert_equal response.cookies, 'user_name' => 'Jamie'
end
def test_setting_with_escapable_characters
diff --git a/actionpack/test/dispatch/live_response_test.rb b/actionpack/test/dispatch/live_response_test.rb
index 87a6b1383d..e16f23914b 100644
--- a/actionpack/test/dispatch/live_response_test.rb
+++ b/actionpack/test/dispatch/live_response_test.rb
@@ -8,6 +8,23 @@ module ActionController
@response = Live::Response.new
end
+ def test_header_merge
+ header = @response.header.merge('Foo' => 'Bar')
+ assert_kind_of(ActionController::Live::Response::Header, header)
+ refute_equal header, @response.header
+ end
+
+ def test_initialize_with_default_headers
+ r = Class.new(Live::Response) do
+ def self.default_headers
+ { 'omg' => 'g' }
+ end
+ end
+
+ header = r.new.header
+ assert_kind_of(ActionController::Live::Response::Header, header)
+ end
+
def test_parallel
latch = ActiveSupport::Concurrency::Latch.new
diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb
index 536e35ab2e..3b008fdff0 100644
--- a/actionpack/test/dispatch/mount_test.rb
+++ b/actionpack/test/dispatch/mount_test.rb
@@ -22,6 +22,7 @@ class TestRoutingMount < ActionDispatch::IntegrationTest
mount SprocketsApp => "/shorthand"
mount FakeEngine, :at => "/fakeengine"
+ mount FakeEngine, :at => "/getfake", :via => :get
scope "/its_a" do
mount SprocketsApp, :at => "/sprocket"
@@ -52,6 +53,14 @@ class TestRoutingMount < ActionDispatch::IntegrationTest
assert_equal "/shorthand -- /omg", response.body
end
+ def test_mounting_works_with_via
+ get "/getfake"
+ assert_equal "OK", response.body
+
+ post "/getfake"
+ assert_response :not_found
+ end
+
def test_with_fake_engine_does_not_call_invalid_method
get "/fakeengine"
assert_equal "OK", response.body
diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb
index ab2f7612ce..6d75c5ec7a 100644
--- a/actionpack/test/dispatch/prefix_generation_test.rb
+++ b/actionpack/test/dispatch/prefix_generation_test.rb
@@ -166,18 +166,6 @@ module TestGenerationPrefix
assert_equal "/generate", last_response.body
end
- test "[ENGINE] generating application's url includes default_url_options[:script_name]" do
- RailsApplication.routes.default_url_options = {:script_name => "/something"}
- get "/pure-awesomeness/blog/url_to_application"
- assert_equal "/something/generate", last_response.body
- end
-
- test "[ENGINE] generating application's url should give higher priority to default_url_options[:script_name]" do
- RailsApplication.routes.default_url_options = {:script_name => "/something"}
- get "/pure-awesomeness/blog/url_to_application", {}, 'SCRIPT_NAME' => '/foo'
- assert_equal "/something/generate", last_response.body
- end
-
test "[ENGINE] generating engine's url with polymorphic path" do
get "/pure-awesomeness/blog/polymorphic_path_for_engine"
assert_equal "/pure-awesomeness/blog/posts/1", last_response.body
@@ -200,12 +188,6 @@ module TestGenerationPrefix
assert_equal "/something/awesome/blog/posts/1", last_response.body
end
- test "[APP] generating engine's route should give higher priority to default_url_options[:script_name]" do
- RailsApplication.routes.default_url_options = {:script_name => "/something"}
- get "/generate", {}, 'SCRIPT_NAME' => "/foo"
- assert_equal "/something/awesome/blog/posts/1", last_response.body
- end
-
test "[APP] generating engine's url with polymorphic path" do
get "/polymorphic_path_for_engine"
assert_equal "/awesome/blog/posts/1", last_response.body
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index e2903d4b36..4d699bd739 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -176,6 +176,35 @@ class ResponseTest < ActiveSupport::TestCase
ActionDispatch::Response.default_charset = original
end
end
+
+ test "read x_frame_options, x_content_type_options and x_xss_protection" do
+ ActionDispatch::Response.default_headers = {
+ 'X-Frame-Options' => 'DENY',
+ 'X-Content-Type-Options' => 'nosniff',
+ 'X-XSS-Protection' => '1;'
+ }
+ resp = ActionDispatch::Response.new.tap { |response|
+ response.body = 'Hello'
+ }
+ resp.to_a
+
+ assert_equal('DENY', resp.headers['X-Frame-Options'])
+ assert_equal('nosniff', resp.headers['X-Content-Type-Options'])
+ assert_equal('1;', resp.headers['X-XSS-Protection'])
+ end
+
+ test "read custom default_header" do
+ ActionDispatch::Response.default_headers = {
+ 'X-XX-XXXX' => 'Here is my phone number'
+ }
+ resp = ActionDispatch::Response.new.tap { |response|
+ response.body = 'Hello'
+ }
+ resp.to_a
+
+ assert_equal('Here is my phone number', resp.headers['X-XX-XXXX'])
+ end
+
end
class ResponseIntegrationTest < ActionDispatch::IntegrationTest
diff --git a/actionpack/test/dispatch/routing/concerns_test.rb b/actionpack/test/dispatch/routing/concerns_test.rb
new file mode 100644
index 0000000000..21da3bd77a
--- /dev/null
+++ b/actionpack/test/dispatch/routing/concerns_test.rb
@@ -0,0 +1,82 @@
+require 'abstract_unit'
+
+class RoutingConcernsTest < ActionDispatch::IntegrationTest
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ concern :commentable do
+ resources :comments
+ end
+
+ concern :image_attachable do
+ resources :images, only: :index
+ end
+
+ resources :posts, concerns: [:commentable, :image_attachable] do
+ resource :video, concerns: :commentable
+ end
+
+ resource :picture, concerns: :commentable do
+ resources :posts, concerns: :commentable
+ end
+
+ scope "/videos" do
+ concerns :commentable
+ end
+ end
+ end
+
+ include Routes.url_helpers
+ def app; Routes end
+
+ def test_accessing_concern_from_resources
+ get "/posts/1/comments"
+ assert_equal "200", @response.code
+ assert_equal "/posts/1/comments", post_comments_path(post_id: 1)
+ end
+
+ def test_accessing_concern_from_resource
+ get "/picture/comments"
+ assert_equal "200", @response.code
+ assert_equal "/picture/comments", picture_comments_path
+ end
+
+ def test_accessing_concern_from_nested_resource
+ get "/posts/1/video/comments"
+ assert_equal "200", @response.code
+ assert_equal "/posts/1/video/comments", post_video_comments_path(post_id: 1)
+ end
+
+ def test_accessing_concern_from_nested_resources
+ get "/picture/posts/1/comments"
+ assert_equal "200", @response.code
+ assert_equal "/picture/posts/1/comments", picture_post_comments_path(post_id: 1)
+ end
+
+ def test_accessing_concern_from_resources_with_more_than_one_concern
+ get "/posts/1/images"
+ assert_equal "200", @response.code
+ assert_equal "/posts/1/images", post_images_path(post_id: 1)
+ end
+
+ def test_accessing_concern_from_resources_using_only_option
+ get "/posts/1/image/1"
+ assert_equal "404", @response.code
+ end
+
+ def test_accessing_concern_from_a_scope
+ get "/videos/comments"
+ assert_equal "200", @response.code
+ end
+
+ def test_with_an_invalid_concern_name
+ e = assert_raise ArgumentError do
+ ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ resources :posts, concerns: :foo
+ end
+ end
+ end
+
+ assert_equal "No concern named foo was found!", e.message
+ end
+end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index d0308b0e74..b029131ad8 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -511,7 +511,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resources :todos, :id => /\d+/
end
- scope '/countries/:country', :constraints => lambda { |params, req| params[:country].in?(["all", "France"]) } do
+ scope '/countries/:country', :constraints => lambda { |params, req| %w(all France).include?(params[:country]) } do
get '/', :to => 'countries#index'
get '/cities', :to => 'countries#cities'
end
diff --git a/actionpack/test/fixtures/test/_raise_indentation.html.erb b/actionpack/test/fixtures/test/_raise_indentation.html.erb
new file mode 100644
index 0000000000..f9a93728fe
--- /dev/null
+++ b/actionpack/test/fixtures/test/_raise_indentation.html.erb
@@ -0,0 +1,13 @@
+<p>First paragraph</p>
+<p>Second paragraph</p>
+<p>Third paragraph</p>
+<p>Fourth paragraph</p>
+<p>Fifth paragraph</p>
+<p>Sixth paragraph</p>
+<p>Seventh paragraph</p>
+<p>Eight paragraph</p>
+<p>Ninth paragraph</p>
+<p>Tenth paragraph</p>
+<%= raise "error here!" %>
+<p>Eleventh paragraph</p>
+<p>Twelfth paragraph</p> \ No newline at end of file
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index bcc55189b9..6a44197525 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -417,6 +417,15 @@ class AssetTagHelperTest < ActionView::TestCase
assert_raise(ArgumentError) { javascript_include_tag(:defaults) }
end
+ def test_all_javascript_expansion_not_include_application_js_if_not_exists
+ FileUtils.mv(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.js'),
+ File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.bak'))
+ assert_no_match(/application\.js/, javascript_include_tag(:all))
+ ensure
+ FileUtils.mv(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.bak'),
+ File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.js'))
+ end
+
def test_stylesheet_path
ENV["RAILS_ASSET_ID"] = ""
StylePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index ff85a675a2..a4da7cd4b0 100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -1243,6 +1243,35 @@ class DateHelperTest < ActionView::TestCase
:prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year', :hour => 'Choose hour', :minute => 'Choose minute'})
end
+ def test_select_datetime_with_custom_hours
+ expected = %(<select id="date_first_year" name="date[first][year]">\n)
+ expected << %(<option value="">Choose year</option>\n<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_first_month" name="date[first][month]">\n)
+ expected << %(<option value="">Choose month</option>\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_first_day" name="date[first][day]">\n)
+ expected << %(<option value="">Choose day</option>\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ expected << " &mdash; "
+
+ expected << %(<select id="date_first_hour" name="date[first][hour]">\n)
+ expected << %(<option value="">Choose hour</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n)
+ expected << "</select>\n"
+
+ expected << " : "
+
+ expected << %(<select id="date_first_minute" name="date[first][minute]">\n)
+ expected << %(<option value="">Choose minute</option>\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), :start_year => 2003, :end_year => 2005, :start_hour => 1, :end_hour => 9, :prefix => "date[first]",
+ :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year', :hour => 'Choose hour', :minute => 'Choose minute'})
+ end
+
def test_select_datetime_with_hidden
expected = %(<input id="date_first_year" name="date[first][year]" type="hidden" value="2003" />\n)
expected << %(<input id="date_first_month" name="date[first][month]" type="hidden" value="8" />\n)
@@ -2629,6 +2658,30 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, datetime_select("post", "updated_at", :discard_minute => true)
end
+ def test_datetime_select_disabled_and_discard_minute
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<select id="post_updated_at_1i" disabled="disabled" name="post[updated_at(1i)]">\n}
+ 1999.upto(2009) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 2004}>#{i}</option>\n) }
+ expected << "</select>\n"
+ expected << %{<select id="post_updated_at_2i" disabled="disabled" name="post[updated_at(2i)]">\n}
+ 1.upto(12) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 6}>#{Date::MONTHNAMES[i]}</option>\n) }
+ expected << "</select>\n"
+ expected << %{<select id="post_updated_at_3i" disabled="disabled" name="post[updated_at(3i)]">\n}
+ 1.upto(31) { |i| expected << %(<option value="#{i}"#{' selected="selected"' if i == 15}>#{i}</option>\n) }
+ expected << "</select>\n"
+
+ expected << " &mdash; "
+
+ expected << %{<select id="post_updated_at_4i" disabled="disabled" name="post[updated_at(4i)]">\n}
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 15}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+ expected << %{<input type="hidden" id="post_updated_at_5i" disabled="disabled" name="post[updated_at(5i)]" value="16" />\n}
+
+ assert_dom_equal expected, datetime_select("post", "updated_at", :discard_minute => true, :disabled => true)
+ end
+
def test_datetime_select_invalid_order
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 15, 16, 35)
diff --git a/actionpack/test/template/erb_util_test.rb b/actionpack/test/template/erb_util_test.rb
index 4132c8ca04..f1cb920963 100644
--- a/actionpack/test/template/erb_util_test.rb
+++ b/actionpack/test/template/erb_util_test.rb
@@ -39,7 +39,7 @@ class ErbUtilTest < ActiveSupport::TestCase
def test_rest_in_ascii
(0..127).to_a.map {|int| int.chr }.each do |chr|
- next if chr.in?('&"<>\'')
+ next if %('"&<>).include?(chr)
assert_equal chr, html_escape(chr)
end
end
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index ab22dbfa44..246c4bfada 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -81,8 +81,6 @@ class FormHelperTest < ActionView::TestCase
@post.tags = []
@post.tags << Tag.new
- @blog_post = Blog::Post.new("And his name will be forty and four.", 44)
-
@car = Car.new("#000FFF")
end
@@ -1045,6 +1043,20 @@ class FormHelperTest < ActionView::TestCase
end
end
+ def test_form_for_requires_arguments
+ error = assert_raises(ArgumentError) do
+ form_for(nil, :html => { :id => 'create-post' }) do
+ end
+ end
+ assert_equal "First argument in form cannot contain nil or be empty", error.message
+
+ error = assert_raises(ArgumentError) do
+ form_for([nil, nil], :html => { :id => 'create-post' }) do
+ end
+ end
+ assert_equal "First argument in form cannot contain nil or be empty", error.message
+ end
+
def test_form_for
form_for(@post, :html => { :id => 'create-post' }) do |f|
concat f.label(:title) { "The Title" }
@@ -1154,7 +1166,9 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_model_using_relative_model_naming
- form_for(@blog_post) do |f|
+ blog_post = Blog::Post.new("And his name will be forty and four.", 44)
+
+ form_for(blog_post) do |f|
concat f.text_field :title
concat f.submit('Edit post')
end
@@ -2637,6 +2651,12 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ def test_form_for_with_data_attributes
+ form_for(@post, data: { behavior: "stuff" }, remote: true) {}
+ assert_match %r|data-behavior="stuff"|, output_buffer
+ assert_match %r|data-remote="true"|, output_buffer
+ end
+
def test_fields_for_returns_block_result
output = fields_for(Post.new) { |f| "fields" }
assert_equal "fields", output
@@ -2659,7 +2679,7 @@ class FormHelperTest < ActionView::TestCase
def hidden_fields(method = nil)
txt = %{<div style="margin:0;padding:0;display:inline">}
txt << %{<input name="utf8" type="hidden" value="&#x2713;" />}
- if method && !method.to_s.in?(['get', 'post'])
+ if method && !%w(get post).include?(method.to_s)
txt << %{<input name="_method" type="hidden" value="#{method}" />}
end
txt << %{</div>}
diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb
index 66a572e719..f908de865e 100644
--- a/actionpack/test/template/form_options_helper_test.rb
+++ b/actionpack/test/template/form_options_helper_test.rb
@@ -82,7 +82,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_collection_options_with_proc_for_disabled
assert_dom_equal(
"<option value=\"&lt;Abe&gt;\">&lt;Abe&gt; went home</option>\n<option value=\"Babe\" disabled=\"disabled\">Babe went home</option>\n<option value=\"Cabe\" disabled=\"disabled\">Cabe went home</option>",
- options_from_collection_for_select(dummy_posts, "author_name", "title", :disabled => lambda{|p| p.author_name.in?(["Babe", "Cabe"]) })
+ options_from_collection_for_select(dummy_posts, "author_name", "title", :disabled => lambda {|p| %w(Babe Cabe).include?(p.author_name)})
)
end
@@ -1124,7 +1124,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_options_for_select_with_element_attributes
assert_dom_equal(
- "<option value=\"&lt;Denmark&gt;\" class=\"bold\">&lt;Denmark&gt;</option>\n<option value=\"USA\" onclick=\"" + ERB::Util.html_escape("alert('Hello World');") + "\">USA</option>\n<option value=\"Sweden\">Sweden</option>\n<option value=\"Germany\">Germany</option>",
+ "<option value=\"&lt;Denmark&gt;\" class=\"bold\">&lt;Denmark&gt;</option>\n<option value=\"USA\" onclick=\"alert(&#x27;Hello World&#x27;);\">USA</option>\n<option value=\"Sweden\">Sweden</option>\n<option value=\"Germany\">Germany</option>",
options_for_select([ [ "<Denmark>", { :class => 'bold' } ], [ "USA", { :onclick => "alert('Hello World');" } ], [ "Sweden" ], "Germany" ])
)
end
@@ -1164,11 +1164,12 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
- def test_option_html_attributes_from_without_hash
- assert_equal(
- {},
- option_html_attributes([ 'foo', 'bar' ])
- )
+ def test_option_html_attributes_with_no_array_element
+ assert_equal({}, option_html_attributes('foo'))
+ end
+
+ def test_option_html_attributes_without_hash
+ assert_equal({}, option_html_attributes([ 'foo', 'bar' ]))
end
def test_option_html_attributes_with_single_element_hash
@@ -1192,6 +1193,15 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_option_html_attributes_with_multiple_hashes_does_not_modify_them
+ options1 = { class: 'fancy' }
+ options2 = { onclick: "alert('Hello World');" }
+ option_html_attributes([ 'foo', 'bar', options1, options2 ])
+
+ assert_equal({ class: 'fancy' }, options1)
+ assert_equal({ onclick: "alert('Hello World');" }, options2)
+ end
+
def test_grouped_collection_select
@post = Post.new
@post.origin = 'dk'
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index dcae5b2352..3c66a29754 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -15,7 +15,7 @@ class FormTagHelperTest < ActionView::TestCase
txt = %{<div style="margin:0;padding:0;display:inline">}
txt << %{<input name="utf8" type="hidden" value="&#x2713;" />}
- if method && !method.to_s.in?(['get','post'])
+ if method && !%w(get post).include?(method.to_s)
txt << %{<input name="_method" type="hidden" value="#{method}" />}
end
txt << %{</div>}
@@ -225,6 +225,18 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal expected, actual
end
+ def test_select_tag_with_nil_option_tags_and_include_blank
+ actual = select_tag "places", nil, :include_blank => true
+ expected = %(<select id="places" name="places"><option value=""></option></select>)
+ assert_dom_equal expected, actual
+ end
+
+ def test_select_tag_with_nil_option_tags_and_prompt
+ actual = select_tag "places", nil, :prompt => "string"
+ expected = %(<select id="places" name="places"><option value="">string</option></select>)
+ assert_dom_equal expected, actual
+ end
+
def test_text_area_tag_size_string
actual = text_area_tag "body", "hello world", "size" => "20x40"
expected = %(<textarea cols="20" id="body" name="body" rows="40">\nhello world</textarea>)
@@ -379,7 +391,7 @@ class FormTagHelperTest < ActionView::TestCase
def test_submit_tag
assert_dom_equal(
- %(<input name='commit' data-disable-with="Saving..." onclick=") + ERB::Util.html_escape("alert('hello!')") + %(" type="submit" value="Save" />),
+ %(<input name='commit' data-disable-with="Saving..." onclick="alert(&#x27;hello!&#x27;)" type="submit" value="Save" />),
submit_tag("Save", :onclick => "alert('hello!')", :data => { :disable_with => "Saving..." })
)
end
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index fe7607ee26..4a9a382afa 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -42,6 +42,48 @@ class JavaScriptHelperTest < ActionView::TestCase
assert_instance_of ActiveSupport::SafeBuffer, escape_javascript(ActiveSupport::SafeBuffer.new(given))
end
+ def test_button_to_function
+ assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do
+ assert_dom_equal %(<input type="button" onclick="alert(&#x27;Hello world!&#x27;);" value="Greeting" />),
+ button_to_function("Greeting", "alert('Hello world!')")
+ end
+ end
+
+ def test_button_to_function_with_onclick
+ assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do
+ assert_dom_equal "<input onclick=\"alert(&#x27;Goodbye World :(&#x27;); alert(&#x27;Hello world!&#x27;);\" type=\"button\" value=\"Greeting\" />",
+ button_to_function("Greeting", "alert('Hello world!')", :onclick => "alert('Goodbye World :(')")
+ end
+ end
+
+ def test_button_to_function_without_function
+ assert_deprecated "button_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do
+ assert_dom_equal "<input onclick=\";\" type=\"button\" value=\"Greeting\" />",
+ button_to_function("Greeting")
+ end
+ end
+
+ def test_link_to_function
+ assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do
+ assert_dom_equal %(<a href="#" onclick="alert(&#x27;Hello world!&#x27;); return false;">Greeting</a>),
+ link_to_function("Greeting", "alert('Hello world!')")
+ end
+ end
+
+ def test_link_to_function_with_existing_onclick
+ assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do
+ assert_dom_equal %(<a href="#" onclick="confirm(&#x27;Sanity!&#x27;); alert(&#x27;Hello world!&#x27;); return false;">Greeting</a>),
+ link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')")
+ end
+ end
+
+ def test_function_with_href
+ assert_deprecated "link_to_function is deprecated and will be removed from Rails 4.1. Use Unobtrusive JavaScript instead." do
+ assert_dom_equal %(<a href="http://example.com/" onclick="alert(&#x27;Hello world!&#x27;); return false;">Greeting</a>),
+ link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/')
+ end
+ end
+
def test_javascript_tag
self.output_buffer = 'foo'
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 164b8b9fa1..b26354e7cc 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -203,6 +203,15 @@ module RenderTestCases
assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
end
+ def test_render_error_indentation
+ e = assert_raises(ActionView::Template::Error) { @view.render(:partial => "test/raise_indentation") }
+ error_lines = e.annoted_source_code.split("\n")
+ assert_match %r!error\shere!, e.message
+ assert_equal "11", e.line_number
+ assert_equal " 9: <p>Ninth paragraph</p>", error_lines.second
+ assert_equal " 10: <p>Tenth paragraph</p>", error_lines.third
+ end
+
def test_render_sub_template_with_errors
e = assert_raises(ActionView::Template::Error) { @view.render(:template => "test/sub_template_raise") }
assert_match %r!method.*doesnt_exist!, e.message
diff --git a/actionpack/test/template/sanitize_helper_test.rb b/actionpack/test/template/sanitize_helper_test.rb
index 4182af590e..7626cdf386 100644
--- a/actionpack/test/template/sanitize_helper_test.rb
+++ b/actionpack/test/template/sanitize_helper_test.rb
@@ -40,9 +40,9 @@ class SanitizeHelperTest < ActionView::TestCase
[nil, '', ' '].each do |blank|
stripped = strip_tags(blank)
assert_equal blank, stripped
- assert stripped.html_safe? unless blank.nil?
end
- assert strip_tags("<script>").html_safe?
+ assert_equal "", strip_tags("<script>")
+ assert_equal "something &lt;img onerror=alert(1337)", ERB::Util.html_escape(strip_tags("something <img onerror=alert(1337)"))
end
def test_sanitize_is_marked_safe
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index 75ec1d8f16..c0f694b2bf 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -149,7 +149,7 @@ class TextHelperTest < ActionView::TestCase
end
def test_truncate_with_block_should_escape_the_block
- assert_equal "Here is a long test and ...&lt;script&gt;" + ERB::Util.html_escape("alert('foo');") + "&lt;/script&gt;",
+ assert_equal "Here is a long test and ...&lt;script&gt;alert(&#x27;foo&#x27;);&lt;/script&gt;",
truncate("Here is a long test and I need a continue to read link", :length => 27) { "<script>alert('foo');</script>" }
end
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 2c67b2210b..f9f8c36fff 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -244,7 +244,7 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_link_tag_with_custom_onclick
link = link_to("Hello", "http://www.example.com", :onclick => "alert('yay!')")
- expected = %{<a href="http://www.example.com" onclick="} + ERB::Util.html_escape("alert('yay!')") + %{">Hello</a>}
+ expected = %{<a href="http://www.example.com" onclick="alert(&#x27;yay!&#x27;)">Hello</a>}
assert_dom_equal expected, link
end
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 847ae7f237..1916c3439a 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,5 +1,22 @@
## Rails 4.0.0 (unreleased) ##
+* Changed inclusion and exclusion validators to accept a symbol for `:in` option.
+
+ This allows to use dynamic inclusion/exclusion values using methods, besides the current lambda/proc support.
+
+ *Gabriel Sobrinho*
+
+* `AM::Validation#validates` ability to pass custom exception to `:strict` option.
+
+ *Bogdan Gusiev*
+
+* Changed `ActiveModel::Serializers::Xml::Serializer#add_associations` to by default
+ propagate `:skip_types, :dasherize, :camelize` keys to included associations.
+ It can be overriden on each association by explicitly specifying the option on one
+ or more associations
+
+ *Anthony Alberto*
+
* Changed `AM::Serializers::JSON.include_root_in_json' default value to false.
Now, AM Serializers and AR objects have the same default behaviour. Fixes #6578.
@@ -39,10 +56,24 @@
* When `^` or `$` are used in the regular expression provided to `validates_format_of` and the :multiline option is not set to true, an exception will be raised. This is to prevent security vulnerabilities when using `validates_format_of`. The problem is described in detail in the Rails security guide.
+
+## 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.5 (Jun 1, 2012) ##
* No changes.
diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb
index ec2d734647..d1cc19ec6b 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -34,7 +34,6 @@ module ActiveModel
autoload :Conversion
autoload :Dirty
autoload :EachValidator, 'active_model/validator'
- autoload :Errors
autoload :Lint
autoload :MassAssignmentSecurity
autoload :Model
@@ -49,11 +48,22 @@ module ActiveModel
autoload :Validations
autoload :Validator
+ eager_autoload do
+ autoload :Errors
+ end
+
module Serializers
extend ActiveSupport::Autoload
- autoload :JSON
- autoload :Xml
+ eager_autoload do
+ autoload :JSON
+ autoload :Xml
+ end
+ end
+
+ def eager_load!
+ super
+ ActiveModel::Serializer.eager_load!
end
end
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index e669113001..e442455a53 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -38,6 +38,17 @@ module ActiveModel
# # Your code here
# end
#
+ # When defining an around callback remember to yield to the block, otherwise
+ # it won't be executed:
+ #
+ # around_create :log_status
+ #
+ # def log_status
+ # puts 'going to call the block...'
+ # yield
+ # puts 'block successfully called.'
+ # end
+ #
# You can choose not to have all three callbacks by passing a hash to the
# +define_model_callbacks+ method.
#
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 1026b0f4d3..b3b9ba8e56 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -283,15 +283,19 @@ module ActiveModel
#
# If the <tt>:strict</tt> option is set to true will raise
# ActiveModel::StrictValidationFailed instead of adding the error.
+ # <tt>:strict</tt> option can also be set to any other exception.
#
# person.errors.add(:name, nil, strict: true)
# # => ActiveModel::StrictValidationFailed: name is invalid
+ # person.errors.add(:name, nil, strict: NameIsInvalid)
+ # # => NameIsInvalid: name is invalid
#
# person.errors.messages # => {}
def add(attribute, message = nil, options = {})
message = normalize_message(attribute, message, options)
- if options[:strict]
- raise ActiveModel::StrictValidationFailed, full_message(attribute, message)
+ if exception = options[:strict]
+ exception = ActiveModel::StrictValidationFailed if exception == true
+ raise exception, full_message(attribute, message)
end
self[attribute] << message
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 7ba439fb3e..c0d93e5d53 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -298,14 +298,15 @@ module ActiveModel
model_name_from_record_or_class(record_or_class).param_key
end
- private
- def self.model_name_from_record_or_class(record_or_class)
- (record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
- end
-
- def self.convert_to_model(object)
- object.respond_to?(:to_model) ? object.to_model : object
+ def self.model_name_from_record_or_class(record_or_class) #:nodoc:
+ if record_or_class.respond_to?(:model_name)
+ record_or_class.model_name
+ elsif record_or_class.respond_to?(:to_model)
+ record_or_class.to_model.class.model_name
+ else
+ record_or_class.class.model_name
end
+ end
+ private_class_method :model_name_from_record_or_class
end
-
end
diff --git a/activemodel/lib/active_model/railtie.rb b/activemodel/lib/active_model/railtie.rb
index 63ffe5db63..f239758b35 100644
--- a/activemodel/lib/active_model/railtie.rb
+++ b/activemodel/lib/active_model/railtie.rb
@@ -1,2 +1,8 @@
require "active_model"
-require "rails" \ No newline at end of file
+require "rails"
+
+module ActiveModel
+ class Railtie < Rails::Railtie
+ config.eager_load_namespaces << ActiveModel
+ end
+end \ No newline at end of file
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index 016d821fdf..cf742d0569 100644..100755
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -115,6 +115,10 @@ module ActiveModel
merged_options = opts.merge(options.slice(:builder, :indent))
merged_options[:skip_instruct] = true
+ [:skip_types, :dasherize, :camelize].each do |key|
+ merged_options[key] = options[key] if merged_options[key].nil? && !options[key].nil?
+ end
+
if records.respond_to?(:to_ary)
records = records.to_ary
diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb
index 643a6f2b7c..cf1415b6c2 100644
--- a/activemodel/lib/active_model/validations/clusivity.rb
+++ b/activemodel/lib/active_model/validations/clusivity.rb
@@ -3,11 +3,11 @@ require 'active_support/core_ext/range.rb'
module ActiveModel
module Validations
module Clusivity #:nodoc:
- ERROR_MESSAGE = "An object with the method #include? or a proc or lambda is required, " <<
+ ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " <<
"and must be supplied as the :in (or :within) option of the configuration hash"
def check_validity!
- unless [:include?, :call].any?{ |method| delimiter.respond_to?(method) }
+ unless delimiter.respond_to?(:include?) || delimiter.respond_to?(:call) || delimiter.respond_to?(:to_sym)
raise ArgumentError, ERROR_MESSAGE
end
end
@@ -15,7 +15,14 @@ module ActiveModel
private
def include?(record, value)
- exclusions = delimiter.respond_to?(:call) ? delimiter.call(record) : delimiter
+ exclusions = if delimiter.respond_to?(:call)
+ delimiter.call(record)
+ elsif delimiter.respond_to?(:to_sym)
+ record.send(delimiter)
+ else
+ delimiter
+ end
+
exclusions.send(inclusion_method(exclusions), value)
end
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index dc3368c569..3ec552c372 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -24,11 +24,12 @@ module ActiveModel
# validates_exclusion_of :format, in: %w( mov avi ), message: "extension %{value} is not allowed"
# validates_exclusion_of :password, in: ->(person) { [person.username, person.first_name] },
# message: 'should not be the same as your username or first name'
+ # validates_exclusion_of :karma, in: :reserved_karmas
# end
#
# Configuration options:
# * <tt>:in</tt> - An enumerable object of items that the value shouldn't
- # be part of. This can be supplied as a proc or lambda which returns an
+ # be part of. This can be supplied as a proc, lambda or symbol which returns an
# enumerable. If the enumerable is a range the test is performed with
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
# <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>.
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index c2835c550b..babc8982da 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -23,11 +23,12 @@ module ActiveModel
# validates_inclusion_of :age, in: 0..99
# validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list"
# validates_inclusion_of :states, in: ->(person) { STATES[person.country] }
+ # validates_inclusion_of :karma, in: :available_karmas
# end
#
# Configuration options:
# * <tt>:in</tt> - An enumerable object of available items. This can be
- # supplied as a proc or lambda which returns an enumerable. If the
+ # supplied as a proc, lambda or symbol which returns an enumerable. If the
# enumerable is a range the test is performed with <tt>Range#cover?</tt>,
# otherwise with <tt>include?</tt>.
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index 5892ad29d1..eb6e604851 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -84,12 +84,15 @@ module ActiveModel
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a +true+ or
# +false+ value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
- # See <tt>ActiveModel::Validation#validates!</tt> for more information.
+ # * <tt>:strict</tt> - if the <tt>:strict</tt> option is set to true
+ # will raise ActiveModel::StrictValidationFailed instead of adding the error.
+ # <tt>:strict</tt> option can also be set to any other exception.
#
# Example:
#
# validates :password, presence: true, confirmation: true, if: :password_required?
+ # validates :token, uniqueness: true, strict: TokenGenerationException
+ #
#
# Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+
# and +:strict+ can be given to one specific validator, as a hash:
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 87e4319421..85aec00f25 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -65,7 +65,7 @@ module ActiveModel
#
# class TitleValidator < ActiveModel::EachValidator
# def validate_each(record, attribute, value)
- # record.errors.add attribute, 'must be Mr. Mrs. or Dr.' unless value.in?(['Mr.', 'Mrs.', 'Dr.'])
+ # record.errors.add attribute, 'must be Mr., Mrs., or Dr.' unless %w(Mr. Mrs. Dr.).include?(value)
# end
# end
#
diff --git a/activemodel/test/cases/mass_assignment_security_test.rb b/activemodel/test/cases/mass_assignment_security_test.rb
index 0c6352cd71..45757615f5 100644
--- a/activemodel/test/cases/mass_assignment_security_test.rb
+++ b/activemodel/test/cases/mass_assignment_security_test.rb
@@ -11,7 +11,6 @@ class CustomSanitizer < ActiveModel::MassAssignmentSecurity::Sanitizer
end
class MassAssignmentSecurityTest < ActiveModel::TestCase
-
def test_attribute_protection
user = User.new
expected = { "name" => "John Smith", "email" => "john@smith.com" }
@@ -96,7 +95,6 @@ class MassAssignmentSecurityTest < ActiveModel::TestCase
assert_blank TightDescendant.protected_attributes(:admin) - TightDescendant.attributes_protected_by_default
assert_equal Set.new(['name', 'address', 'admin', 'super_powers']), TightDescendant.accessible_attributes(:admin)
-
end
def test_mass_assignment_multiparameter_protector
@@ -107,14 +105,14 @@ class MassAssignmentSecurityTest < ActiveModel::TestCase
end
def test_custom_sanitizer
+ old_sanitizer = User._mass_assignment_sanitizer
+
user = User.new
User.mass_assignment_sanitizer = CustomSanitizer.new
assert_raise StandardError do
user.sanitize_for_mass_assignment("admin" => true)
end
ensure
- User.mass_assignment_sanitizer = nil
-
+ User.mass_assignment_sanitizer = old_sanitizer
end
-
-end
+end \ No newline at end of file
diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb
index a93323a3a8..8c5a3c5efd 100644..100755
--- a/activemodel/test/cases/serializers/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializers/xml_serialization_test.rb
@@ -28,7 +28,7 @@ class Address
extend ActiveModel::Naming
include ActiveModel::Serializers::Xml
- attr_accessor :street, :city, :state, :zip
+ attr_accessor :street, :city, :state, :zip, :apt_number
def attributes
instance_values
@@ -56,6 +56,7 @@ class XmlSerializationTest < ActiveModel::TestCase
@contact.address.city = "Springfield"
@contact.address.state = "CA"
@contact.address.zip = 11111
+ @contact.address.apt_number = 35
@contact.friends = [Contact.new, Contact.new]
end
@@ -222,4 +223,39 @@ class XmlSerializationTest < ActiveModel::TestCase
assert_match %r{<friends>}, xml
assert_match %r{<friend>}, xml
end
+
+ test "propagates skip-types option to included associations and attributes" do
+ xml = @contact.to_xml :skip_types => true, :include => :address, :indent => 0
+ assert_match %r{<address>}, xml
+ assert_match %r{<apt-number>}, xml
+ end
+
+ test "propagates camelize option to included associations and attributes" do
+ xml = @contact.to_xml :camelize => true, :include => :address, :indent => 0
+ assert_match %r{<Address>}, xml
+ assert_match %r{<AptNumber type="integer">}, xml
+ end
+
+ test "propagates dasherize option to included associations and attributes" do
+ xml = @contact.to_xml :dasherize => false, :include => :address, :indent => 0
+ assert_match %r{<apt_number type="integer">}, xml
+ end
+
+ test "don't propagate skip_types if skip_types is defined at the included association level" do
+ xml = @contact.to_xml :skip_types => true, :include => { :address => { :skip_types => false } }, :indent => 0
+ assert_match %r{<address>}, xml
+ assert_match %r{<apt-number type="integer">}, xml
+ end
+
+ test "don't propagate camelize if camelize is defined at the included association level" do
+ xml = @contact.to_xml :camelize => true, :include => { :address => { :camelize => false } }, :indent => 0
+ assert_match %r{<address>}, xml
+ assert_match %r{<apt-number type="integer">}, xml
+ end
+
+ test "don't propagate dasherize if dasherize is defined at the included association level" do
+ xml = @contact.to_xml :dasherize => false, :include => { :address => { :dasherize => true } }, :indent => 0
+ assert_match %r{<address>}, xml
+ assert_match %r{<apt-number type="integer">}, xml
+ end
end
diff --git a/activemodel/test/cases/validations/exclusion_validation_test.rb b/activemodel/test/cases/validations/exclusion_validation_test.rb
index baccf72ecb..9f916dbfdd 100644
--- a/activemodel/test/cases/validations/exclusion_validation_test.rb
+++ b/activemodel/test/cases/validations/exclusion_validation_test.rb
@@ -64,4 +64,26 @@ class ExclusionValidationTest < ActiveModel::TestCase
t.title = "wasabi"
assert t.valid?
end
+
+ def test_validates_inclusion_of_with_symbol
+ Person.validates_exclusion_of :karma, :in => :reserved_karmas
+
+ p = Person.new
+ p.karma = "abe"
+
+ def p.reserved_karmas
+ %w(abe)
+ end
+
+ assert p.invalid?
+ assert_equal ["is reserved"], p.errors[:karma]
+
+ def p.reserved_karmas
+ %w()
+ end
+
+ assert p.valid?
+ ensure
+ Person.reset_callbacks(:validate)
+ end
end
diff --git a/activemodel/test/cases/validations/inclusion_validation_test.rb b/activemodel/test/cases/validations/inclusion_validation_test.rb
index c57fa75faf..92638f8e70 100644
--- a/activemodel/test/cases/validations/inclusion_validation_test.rb
+++ b/activemodel/test/cases/validations/inclusion_validation_test.rb
@@ -96,4 +96,26 @@ class InclusionValidationTest < ActiveModel::TestCase
t.title = "elephant"
assert t.valid?
end
+
+ def test_validates_inclusion_of_with_symbol
+ Person.validates_inclusion_of :karma, :in => :available_karmas
+
+ p = Person.new
+ p.karma = "Lifo"
+
+ def p.available_karmas
+ %w()
+ end
+
+ assert p.invalid?
+ assert_equal ["is not included in the list"], p.errors[:karma]
+
+ def p.available_karmas
+ %w(Lifo)
+ end
+
+ assert p.valid?
+ ensure
+ Person.reset_callbacks(:validate)
+ end
end
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index 8ea9745fbf..a9d32808da 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -11,6 +11,8 @@ require 'active_support/xml_mini'
class ValidationsTest < ActiveModel::TestCase
+ class CustomStrictValidationException < StandardError; end
+
def setup
Topic._validators.clear
end
@@ -323,6 +325,13 @@ class ValidationsTest < ActiveModel::TestCase
end
end
+ def test_strict_validation_custom_exception
+ Topic.validates_presence_of :title, :strict => CustomStrictValidationException
+ assert_raises CustomStrictValidationException do
+ Topic.new.valid?
+ end
+ end
+
def test_validates_with_bang
Topic.validates! :title, :presence => true
assert_raises ActiveModel::StrictValidationFailed do
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 8275577467..6fb9097809 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,54 @@
## Rails 4.0.0 (unreleased) ##
+* Allow to pass Symbol or Proc into :limit option of #accepts_nested_attributes_for
+
+ *Mikhail Dieterle*
+
+* ActiveRecord::SessionStore has been extracted from Active Record as `activerecord-session_store`
+ gem. Please read the `README.md` file on the gem for the usage. *Prem Sichanugrist*
+
+* Fix `reset_counters` when there are multiple `belongs_to` association with the
+ same foreign key and one of them have a counter cache.
+ Fixes #5200.
+
+ *Dave Desrochers*
+
+* `serialized_attributes` and `_attr_readonly` become class method only. Instance reader methods are deprecated.
+
+ *kennyj*
+
+* Round usec when comparing timestamp attributes in the dirty tracking.
+ Fixes #6975.
+
+ *kennyj*
+
+* Use inversed parent for first and last child of has_many association.
+
+ *Ravil Bayramgalin*
+
+* Fix Column.microseconds and Column.fast_string_to_date to avoid converting
+ timestamp seconds to a float, since it occasionally results in inaccuracies
+ with microsecond-precision times. Fixes #7352.
+
+ *Ari Pollak*
+
+* Raise `ArgumentError` if list of attributes to change is empty in `update_all`.
+
+ *Roman Shatsov*
+
+* Fix AR#create to return an unsaved record when AR::RecordInvalid is
+ raised. Fixes #3217.
+
+ *Dave Yeu*
+
+* Fixed table name prefix that is generated in engines for namespaced models
+ *Wojciech Wnętrzak*
+
+* Make sure `:environment` task is executed before `db:schema:load` or `db:structure:load`
+ Fixes #4772.
+
+ *Seamus Abshere*
+
* Allow Relation#merge to take a proc.
This was requested by DHH to allow creating of one's own custom
@@ -277,7 +326,7 @@
`where(...).first_or_create!`
The implementation of the deprecated dynamic finders has been moved
- to the `active_record_deprecated_finders` gem. See below for details.
+ to the `activerecord-deprecated_finders` gem. See below for details.
*Jon Leighton*
@@ -308,7 +357,7 @@
* `:extend` becomes `:extending`
The code to implement the deprecated features has been moved out to
- the `active_record_deprecated_finders` gem. This gem is a dependency
+ the `activerecord-deprecated_finders` gem. This gem is a dependency
of Active Record in Rails 4.0. It will no longer be a dependency
from Rails 4.1, but if your app relies on the deprecated features
then you can add it to your own Gemfile. It will be maintained by
@@ -460,11 +509,11 @@
The `add_index` method now supports a `where` option that receives a
string with the partial index criteria.
- add_index(:accounts, :code, :where => "active")
+ add_index(:accounts, :code, :where => "active")
- Generates
+ Generates
- CREATE INDEX index_accounts_on_code ON accounts(code) WHERE active
+ CREATE INDEX index_accounts_on_code ON accounts(code) WHERE active
*Marcelo Silveira*
@@ -481,24 +530,13 @@
* Added the `ActiveRecord::NullRelation` class implementing the null
object pattern for the Relation class. *Juanjo Bazán*
-* Added deprecation for the `:dependent => :restrict` association option.
-
- Please note:
-
- * Up until now `has_many` and `has_one`, `:dependent => :restrict`
- option raised a `DeleteRestrictionError` at the time of destroying
- the object. Instead, it will add an error on the model.
+* Added new `:dependent => :restrict_with_error` option. This will add
+ an error to the model, rather than raising an exception.
- * To fix this warning, make sure your code isn't relying on a
- `DeleteRestrictionError` and then add
- `config.active_record.dependent_restrict_raises = false` to your
- application config.
+ The `:restrict` option is renamed to `:restrict_with_exception` to
+ make this distinction explicit.
- * New rails application would be generated with the
- `config.active_record.dependent_restrict_raises = false` in the
- application config.
-
- *Manoj Kumar*
+ *Manoj Kumar & Jon Leighton*
* Added `create_join_table` migration helper to create HABTM join tables
@@ -584,6 +622,79 @@
* PostgreSQL hstore types are automatically deserialized from the database.
+## Rails 3.2.8 (Aug 9, 2012) ##
+
+* Do not consider the numeric attribute as changed if the old value is zero and the new value
+ is not a string.
+ Fixes #7237.
+
+ *Rafael Mendonça França*
+
+* Removes the deprecation of `update_attribute`. *fxn*
+
+* Reverted the deprecation of `composed_of`. *Rafael Mendonça França*
+
+* Reverted the deprecation of `*_sql` association options. They will
+ be deprecated in 4.0 instead. *Jon Leighton*
+
+* Do not eager load AR session store. ActiveRecord::SessionStore depends on the abstract store
+ in Action Pack. Eager loading this class would break client code that eager loads Active Record
+ standalone.
+ Fixes #7160
+
+ *Xavier Noria*
+
+* Do not set RAILS_ENV to "development" when using `db:test:prepare` and related rake tasks.
+ This was causing the truncation of the development database data when using RSpec.
+ Fixes #7175.
+
+ *Rafael Mendonça França*
+
+
+## Rails 3.2.7 (Jul 26, 2012) ##
+
+* `:finder_sql` and `:counter_sql` options on collection associations
+ are deprecated. Please transition to using scopes.
+
+ *Jon Leighton*
+
+* `:insert_sql` and `:delete_sql` options on `has_and_belongs_to_many`
+ associations are deprecated. Please transition to using `has_many
+ :through`
+
+ *Jon Leighton*
+
+* `composed_of` has been deprecated. You'll have to write your own accessor
+ and mutator methods if you'd like to use value objects to represent some
+ portion of your models.
+
+ *Steve Klabnik*
+
+* `update_attribute` has been deprecated. Use `update_column` if
+ you want to bypass mass-assignment protection, validations, callbacks,
+ and touching of updated_at. Otherwise please use `update_attributes`.
+
+ *Steve Klabnik*
+
+
+## Rails 3.2.6 (Jun 12, 2012) ##
+
+* protect against the nesting of hashes changing the
+ table context in the next call to build_from_hash. This fix
+ covers this case as well.
+
+ CVE-2012-2695
+
+* Revert earlier 'perf fix' (see 3.2.4 changelog / GH #6289). This
+ change introduced a regression (GH #6609). assoc.clear and
+ assoc.delete_all have loaded the association before doing the delete
+ since at least Rails 2.3. Doing the delete without loading the
+ records means that the `before_remove` and `after_remove` callbacks do
+ not get invoked. Therefore, this change was less a fix a more an
+ optimisation, which should only have gone into master.
+
+ *Jon Leighton*
+
## Rails 3.2.5 (Jun 1, 2012) ##
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec
index dca7f13fd2..53791d96ef 100644
--- a/activerecord/activerecord.gemspec
+++ b/activerecord/activerecord.gemspec
@@ -24,5 +24,5 @@ Gem::Specification.new do |s|
s.add_dependency('activemodel', version)
s.add_dependency('arel', '~> 3.0.2')
- s.add_dependency('active_record_deprecated_finders', '0.0.1')
+ s.add_dependency('activerecord-deprecated_finders', '0.0.1')
end
diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb
index 31f3e02bb8..cd9825b50c 100644
--- a/activerecord/examples/performance.rb
+++ b/activerecord/examples/performance.rb
@@ -1,7 +1,9 @@
-TIMES = (ENV['N'] || 10000).to_i
-
require File.expand_path('../../../load_paths', __FILE__)
require "active_record"
+require 'benchmark/ips'
+
+TIME = (ENV['BENCHMARK_TIME'] || 20).to_i
+RECORDS = (ENV['BENCHMARK_RECORDS'] || TIME*1000).to_i
conn = { :adapter => 'sqlite3', :database => ':memory:' }
@@ -72,8 +74,8 @@ end
notes = ActiveRecord::Faker::LOREM.join ' '
today = Date.today
-puts 'Inserting 10,000 users and exhibits...'
-10_000.times do
+puts "Inserting #{RECORDS} users and exhibits..."
+RECORDS.times do
user = User.create(
:created_at => today,
:name => ActiveRecord::Faker.name,
@@ -88,9 +90,7 @@ puts 'Inserting 10,000 users and exhibits...'
)
end
-require 'benchmark'
-
-Benchmark.bm(46) do |x|
+Benchmark.ips(TIME) do |x|
ar_obj = Exhibit.find(1)
attrs = { :name => 'sam' }
attrs_first = { :name => 'sam' }
@@ -101,77 +101,72 @@ Benchmark.bm(46) do |x|
:created_at => Date.today
}
- x.report("Model#id (x#{(TIMES * 100).ceil})") do
- (TIMES * 100).ceil.times { ar_obj.id }
+ x.report("Model#id") do
+ ar_obj.id
end
x.report 'Model.new (instantiation)' do
- TIMES.times { Exhibit.new }
+ Exhibit.new
end
x.report 'Model.new (setting attributes)' do
- TIMES.times { Exhibit.new(attrs) }
+ Exhibit.new(attrs)
end
x.report 'Model.first' do
- TIMES.times { Exhibit.first.look }
+ Exhibit.first.look
end
- x.report 'Model.named_scope' do
- TIMES.times { Exhibit.limit(10).with_name.with_notes }
+ x.report("Model.all limit(100)") do
+ Exhibit.look Exhibit.limit(100)
end
- x.report("Model.all limit(100) (x#{(TIMES / 10).ceil})") do
- (TIMES / 10).ceil.times { Exhibit.look Exhibit.limit(100) }
+ x.report "Model.all limit(100) with relationship" do
+ Exhibit.feel Exhibit.limit(100).includes(:user)
end
- x.report "Model.all limit(100) with relationship (x#{(TIMES / 10).ceil})" do
- (TIMES / 10).ceil.times { Exhibit.feel Exhibit.limit(100).includes(:user) }
+ x.report "Model.all limit(10,000)" do
+ Exhibit.look Exhibit.limit(10000)
end
- x.report "Model.all limit(10,000) x(#{(TIMES / 1000).ceil})" do
- (TIMES / 1000).ceil.times { Exhibit.look Exhibit.limit(10000) }
+ x.report 'Model.named_scope' do
+ Exhibit.limit(10).with_name.with_notes
end
x.report 'Model.create' do
- TIMES.times { Exhibit.create(exhibit) }
+ Exhibit.create(exhibit)
end
x.report 'Resource#attributes=' do
- TIMES.times {
- exhibit = Exhibit.new(attrs_first)
- exhibit.attributes = attrs_second
- }
+ e = Exhibit.new(attrs_first)
+ e.attributes = attrs_second
end
x.report 'Resource#update' do
- TIMES.times { Exhibit.first.update_attributes(:name => 'bob') }
+ Exhibit.first.update_attributes(:name => 'bob')
end
x.report 'Resource#destroy' do
- TIMES.times { Exhibit.first.destroy }
+ Exhibit.first.destroy
end
x.report 'Model.transaction' do
- TIMES.times { Exhibit.transaction { Exhibit.new } }
+ Exhibit.transaction { Exhibit.new }
end
x.report 'Model.find(id)' do
- id = Exhibit.first.id
- TIMES.times { Exhibit.find(id) }
+ User.find(1)
end
x.report 'Model.find_by_sql' do
- TIMES.times {
- Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first
- }
+ Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first
end
- x.report "Model.log x(#{TIMES * 10})" do
- (TIMES * 10).times { Exhibit.connection.send(:log, "hello", "world") {} }
+ x.report "Model.log" do
+ Exhibit.connection.send(:log, "hello", "world") {}
end
- x.report "AR.execute(query) (#{TIMES / 2})" do
- (TIMES / 2).times { ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}") }
+ x.report "AR.execute(query)" do
+ ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}")
end
end
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 5a51aaaced..1675127ab0 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -25,7 +25,7 @@ require 'active_support'
require 'active_support/rails'
require 'active_model'
require 'arel'
-require 'active_record_deprecated_finders'
+require 'active_record/deprecated_finders'
require 'active_record/version'
@@ -53,17 +53,6 @@ module ActiveRecord
autoload :ReadonlyAttributes
autoload :Reflection
autoload :Sanitization
-
- # ActiveRecord::SessionStore depends on the abstract store in Action Pack.
- # Eager loading this class would break client code that eager loads Active
- # Record standalone.
- #
- # Note that the Rails application generator creates an initializer specific
- # for setting the session store. Thus, albeit in theory this autoload would
- # not be thread-safe, in practice it is because if the application uses this
- # session store its autoload happens at boot time.
- autoload :SessionStore
-
autoload :Schema
autoload :SchemaDumper
autoload :SchemaMigration
@@ -160,6 +149,15 @@ module ActiveRecord
autoload :TestCase
autoload :TestFixtures, 'active_record/fixtures'
+
+ def self.eager_load!
+ super
+ ActiveRecord::Locking.eager_load!
+ ActiveRecord::Scoping.eager_load!
+ ActiveRecord::Associations.eager_load!
+ ActiveRecord::AttributeMethods.eager_load!
+ ActiveRecord::ConnectionAdapters.eager_load!
+ end
end
ActiveSupport.on_load(:active_record) do
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index f5ee4f3ebe..d6b8552e6e 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1094,12 +1094,14 @@ module ActiveRecord
# [:primary_key]
# Specify the method that returns the primary key used for the association. By default this is +id+.
# [:dependent]
- # If set to <tt>:destroy</tt> all the associated objects are destroyed
- # alongside this object by calling their +destroy+ method. If set to <tt>:delete_all</tt> all associated
- # objects are deleted *without* calling their +destroy+ method. If set to <tt>:nullify</tt> all associated
- # objects' foreign keys are set to +NULL+ *without* calling their +save+ callbacks. If set to
- # <tt>:restrict</tt> an error will be added to the object, preventing its deletion, if any associated
- # objects are present.
+ # Controls what happens to the associated objects when
+ # their owner is destroyed:
+ #
+ # * <tt>:destroy</tt> causes all the associated objects to also be destroyed
+ # * <tt>:delete_all</tt> causes all the asssociated objects to be deleted directly from the database (so callbacks will not execute)
+ # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Callbacks are not executed.
+ # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there are any associated records
+ # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects
#
# If using with the <tt>:through</tt> option, the association on the join model must be
# a +belongs_to+, and the records which get deleted are the join records, rather than
@@ -1135,7 +1137,9 @@ module ActiveRecord
# [:autosave]
# If true, always save the associated objects or destroy them if marked for destruction,
# when saving the parent object. If false, never save or destroy the associated objects.
- # By default, only save associated objects that are new records.
+ # By default, only save associated objects that are new records. This option is implemented as a
+ # before_save callback. Because callbacks are run in the order they are defined, associated objects
+ # may need to be explicitly saved in any user-defined before_save callbacks.
#
# Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
# [:inverse_of]
@@ -1203,11 +1207,14 @@ module ActiveRecord
# from the association name. So <tt>has_one :manager</tt> will by default be linked to the Manager class, but
# if the real class name is Person, you'll have to specify it with this option.
# [:dependent]
- # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
- # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
- # If set to <tt>:nullify</tt>, the associated object's foreign key is set to +NULL+.
- # If set to <tt>:restrict</tt>, an error will be added to the object, preventing its deletion, if an
- # associated object is present.
+ # Controls what happens to the associated object when
+ # its owner is destroyed:
+ #
+ # * <tt>:destroy</tt> causes the associated object to also be destroyed
+ # * <tt>:delete</tt> causes the asssociated object to be deleted directly from the database (so callbacks will not execute)
+ # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Callbacks are not executed.
+ # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there is an associated record
+ # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object
# [:foreign_key]
# Specify the foreign key used for the association. By default this is guessed to be the name
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 4db7038d2e..9f47e7e631 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -176,7 +176,7 @@ module ActiveRecord
def creation_attributes
attributes = {}
- if reflection.macro.in?([:has_one, :has_many]) && !options[:through]
+ if (reflection.macro == :has_one || reflection.macro == :has_many) && !options[:through]
attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
if reflection.options[:as]
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index ddfc6f6c05..75f72c1a46 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -2,6 +2,11 @@ module ActiveRecord
# = Active Record Belongs To Associations
module Associations
class BelongsToAssociation < SingularAssociation #:nodoc:
+
+ def handle_dependency
+ target.send(options[:dependent]) if load_target
+ end
+
def replace(record)
raise_on_type_mismatch(record) if record
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index c3f32b5ed9..1df876bf62 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -39,6 +39,7 @@ module ActiveRecord::Associations::Builder
def build
validate_options
define_accessors
+ configure_dependency if options[:dependent]
@reflection = model.create_reflection(macro, name, scope, options, model)
super # provides an extension point
@reflection
@@ -52,70 +53,54 @@ module ActiveRecord::Associations::Builder
Association.valid_options
end
- private
-
- def validate_options
- options.assert_valid_keys(valid_options)
- end
+ def validate_options
+ options.assert_valid_keys(valid_options)
+ end
- def define_accessors
- define_readers
- define_writers
- end
+ def define_accessors
+ define_readers
+ define_writers
+ end
- def define_readers
- name = self.name
- mixin.redefine_method(name) do |*params|
- association(name).reader(*params)
+ def define_readers
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def #{name}(*args)
+ association(:#{name}).reader(*args)
end
- end
+ CODE
+ end
- def define_writers
- name = self.name
- mixin.redefine_method("#{name}=") do |value|
- association(name).writer(value)
+ def define_writers
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def #{name}=(value)
+ association(:#{name}).writer(value)
end
- end
-
- def check_valid_dependent!(dependent, valid_options)
- unless valid_options.include?(dependent)
- valid_options_message = valid_options.map(&:inspect).to_sentence(
- words_connector: ', ', two_words_connector: ' or ', last_word_connector: ' or ')
+ CODE
+ end
- raise ArgumentError, "The :dependent option expects either " \
- "#{valid_options_message} (#{dependent.inspect})"
- end
+ def configure_dependency
+ unless valid_dependent_options.include? options[:dependent]
+ raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{options[:dependent]}"
end
- def dependent_restrict_raises?
- ActiveRecord::Base.dependent_restrict_raises == true
+ if options[:dependent] == :restrict
+ ActiveSupport::Deprecation.warn(
+ "The :restrict option is deprecated. Please use :restrict_with_exception instead, which " \
+ "provides the same functionality."
+ )
end
- def dependent_restrict_deprecation_warning
- if dependent_restrict_raises?
- msg = "In the next release, `:dependent => :restrict` will not raise a `DeleteRestrictionError`. "\
- "Instead, it will add an error on the model. To fix this warning, make sure your code " \
- "isn't relying on a `DeleteRestrictionError` and then add " \
- "`config.active_record.dependent_restrict_raises = false` to your application config."
- ActiveSupport::Deprecation.warn msg
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def #{macro}_dependent_for_#{name}
+ association(:#{name}).handle_dependency
end
- end
+ CODE
- def define_restrict_dependency_method
- name = self.name
- mixin.redefine_method(dependency_method_name) do
- has_one_macro = association(name).reflection.macro == :has_one
- if has_one_macro ? !send(name).nil? : send(name).exists?
- if dependent_restrict_raises?
- raise ActiveRecord::DeleteRestrictionError.new(name)
- else
- key = has_one_macro ? "one" : "many"
- errors.add(:base, :"restrict_dependent_destroy.#{key}",
- :record => self.class.human_attribute_name(name).downcase)
- return false
- end
- end
- end
- end
+ model.before_destroy "#{macro}_dependent_for_#{name}"
+ end
+
+ def valid_dependent_options
+ raise NotImplementedError
+ end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index f205a456f7..2f2600b7fb 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -1,4 +1,3 @@
-
module ActiveRecord::Associations::Builder
class BelongsTo < SingularAssociation #:nodoc:
def macro
@@ -17,72 +16,51 @@ module ActiveRecord::Associations::Builder
reflection = super
add_counter_cache_callbacks(reflection) if options[:counter_cache]
add_touch_callbacks(reflection) if options[:touch]
- configure_dependency
reflection
end
- private
-
- def add_counter_cache_callbacks(reflection)
- cache_column = reflection.counter_cache_column
- name = self.name
+ def add_counter_cache_callbacks(reflection)
+ cache_column = reflection.counter_cache_column
- method_name = "belongs_to_counter_cache_after_create_for_#{name}"
- mixin.redefine_method(method_name) do
- record = send(name)
- record.class.increment_counter(cache_column, record.id) unless record.nil?
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def belongs_to_counter_cache_after_create_for_#{name}
+ record = #{name}
+ record.class.increment_counter(:#{cache_column}, record.id) unless record.nil?
end
- model.after_create(method_name)
- method_name = "belongs_to_counter_cache_before_destroy_for_#{name}"
- mixin.redefine_method(method_name) do
+ def belongs_to_counter_cache_before_destroy_for_#{name}
unless marked_for_destruction?
- record = send(name)
- record.class.decrement_counter(cache_column, record.id) unless record.nil?
+ record = #{name}
+ record.class.decrement_counter(:#{cache_column}, record.id) unless record.nil?
end
end
- model.before_destroy(method_name)
+ CODE
- model.send(:module_eval,
- "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)", __FILE__, __LINE__
- )
- end
+ model.after_create "belongs_to_counter_cache_after_create_for_#{name}"
+ model.before_destroy "belongs_to_counter_cache_before_destroy_for_#{name}"
- def add_touch_callbacks(reflection)
- name = self.name
- method_name = "belongs_to_touch_after_save_or_destroy_for_#{name}"
- touch = options[:touch]
+ klass = reflection.class_name.safe_constantize
+ klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
+ end
- mixin.redefine_method(method_name) do
- record = send(name)
+ def add_touch_callbacks(reflection)
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def belongs_to_touch_after_save_or_destroy_for_#{name}
+ record = #{name}
unless record.nil?
- if touch == true
- record.touch
- else
- record.touch(touch)
- end
+ record.touch #{options[:touch].inspect if options[:touch] != true}
end
end
+ CODE
- model.after_save(method_name)
- model.after_touch(method_name)
- model.after_destroy(method_name)
- end
-
- def configure_dependency
- if dependent = options[:dependent]
- check_valid_dependent! dependent, [:destroy, :delete]
+ model.after_save "belongs_to_touch_after_save_or_destroy_for_#{name}"
+ model.after_touch "belongs_to_touch_after_save_or_destroy_for_#{name}"
+ model.after_destroy "belongs_to_touch_after_save_or_destroy_for_#{name}"
+ end
- method_name = "belongs_to_dependent_#{dependent}_for_#{name}"
- model.send(:class_eval, <<-eoruby, __FILE__, __LINE__ + 1)
- def #{method_name}
- association = #{name}
- association.#{dependent} if association
- end
- eoruby
- model.after_destroy method_name
- end
- end
+ def valid_dependent_options
+ [:destroy, :delete]
+ end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index 3fb0a57450..1b382f7285 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -1,4 +1,3 @@
-
module ActiveRecord::Associations::Builder
class CollectionAssociation < Association #:nodoc:
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
@@ -34,53 +33,53 @@ module ActiveRecord::Associations::Builder
end
end
- private
-
- def wrap_block_extension
- if block_extension
- @extension_module = mod = Module.new(&block_extension)
- silence_warnings do
- model.parent.const_set(extension_module_name, mod)
- end
+ def wrap_block_extension
+ if block_extension
+ @extension_module = mod = Module.new(&block_extension)
+ silence_warnings do
+ model.parent.const_set(extension_module_name, mod)
+ end
- prev_scope = @scope
+ prev_scope = @scope
- if prev_scope
- @scope = proc { |owner| instance_exec(owner, &prev_scope).extending(mod) }
- else
- @scope = proc { extending(mod) }
- end
+ if prev_scope
+ @scope = proc { |owner| instance_exec(owner, &prev_scope).extending(mod) }
+ else
+ @scope = proc { extending(mod) }
end
end
+ end
- def extension_module_name
- @extension_module_name ||= "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
- end
+ def extension_module_name
+ @extension_module_name ||= "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
+ end
- def define_callback(callback_name)
- full_callback_name = "#{callback_name}_for_#{name}"
+ def define_callback(callback_name)
+ full_callback_name = "#{callback_name}_for_#{name}"
- # TODO : why do i need method_defined? I think its because of the inheritance chain
- model.class_attribute full_callback_name.to_sym unless model.method_defined?(full_callback_name)
- model.send("#{full_callback_name}=", Array(options[callback_name.to_sym]))
- end
+ # TODO : why do i need method_defined? I think its because of the inheritance chain
+ model.class_attribute full_callback_name.to_sym unless model.method_defined?(full_callback_name)
+ model.send("#{full_callback_name}=", Array(options[callback_name.to_sym]))
+ end
- def define_readers
- super
+ def define_readers
+ super
- name = self.name
- mixin.redefine_method("#{name.to_s.singularize}_ids") do
- association(name).ids_reader
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def #{name.to_s.singularize}_ids
+ association(:#{name}).ids_reader
end
- end
+ CODE
+ end
- def define_writers
- super
+ def define_writers
+ super
- name = self.name
- mixin.redefine_method("#{name.to_s.singularize}_ids=") do |ids|
- association(name).ids_writer(ids)
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def #{name.to_s.singularize}_ids=(ids)
+ association(:#{name}).ids_writer(ids)
end
- end
+ CODE
+ end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index 8df28ad876..bdac02b5bf 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -24,18 +24,16 @@ module ActiveRecord::Associations::Builder
end
end
- private
-
- def define_destroy_hook
- name = self.name
- model.send(:include, Module.new {
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def destroy_associations
- association(#{name.to_sym.inspect}).delete_all
- super
- end
- RUBY
- })
- end
+ def define_destroy_hook
+ name = self.name
+ model.send(:include, Module.new {
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def destroy_associations
+ association(:#{name}).delete_all
+ super
+ end
+ RUBY
+ })
+ end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb
index 9e60dbc30b..ab8225460a 100644
--- a/activerecord/lib/active_record/associations/builder/has_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_many.rb
@@ -1,4 +1,3 @@
-
module ActiveRecord::Associations::Builder
class HasMany < CollectionAssociation #:nodoc:
def macro
@@ -9,52 +8,8 @@ module ActiveRecord::Associations::Builder
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of]
end
- def build
- reflection = super
- configure_dependency
- reflection
+ def valid_dependent_options
+ [:destroy, :delete_all, :nullify, :restrict, :restrict_with_error, :restrict_with_exception]
end
-
- private
-
- def configure_dependency
- if dependent = options[:dependent]
- check_valid_dependent! dependent, [:destroy, :delete_all, :nullify, :restrict]
- dependent_restrict_deprecation_warning if dependent == :restrict
-
- send("define_#{dependent}_dependency_method")
- model.before_destroy dependency_method_name
- end
- end
-
- def define_destroy_dependency_method
- name = self.name
- mixin.redefine_method(dependency_method_name) do
- send(name).each do |o|
- # No point in executing the counter update since we're going to destroy the parent anyway
- o.mark_for_destruction
- end
-
- send(name).delete_all
- end
- end
-
- def define_delete_all_dependency_method
- name = self.name
- mixin.redefine_method(dependency_method_name) do
- association(name).delete_all
- end
- end
-
- def define_nullify_dependency_method
- name = self.name
- mixin.redefine_method(dependency_method_name) do
- send(name).delete_all
- end
- end
-
- def dependency_method_name
- "has_many_dependent_for_#{name}"
- end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb
index 9c84f1913a..0da564f402 100644
--- a/activerecord/lib/active_record/associations/builder/has_one.rb
+++ b/activerecord/lib/active_record/associations/builder/has_one.rb
@@ -1,4 +1,3 @@
-
module ActiveRecord::Associations::Builder
class HasOne < SingularAssociation #:nodoc:
def macro
@@ -15,35 +14,12 @@ module ActiveRecord::Associations::Builder
!options[:through]
end
- def build
- reflection = super
- configure_dependency unless options[:through]
- reflection
+ def configure_dependency
+ super unless options[:through]
end
- private
-
- def configure_dependency
- if dependent = options[:dependent]
- check_valid_dependent! dependent, [:destroy, :delete, :nullify, :restrict]
- dependent_restrict_deprecation_warning if dependent == :restrict
-
- send("define_#{dependent}_dependency_method")
- model.before_destroy dependency_method_name
- end
- end
-
- def define_destroy_dependency_method
- name = self.name
- mixin.redefine_method(dependency_method_name) do
- association(name).delete
- end
- end
- alias :define_delete_dependency_method :define_destroy_dependency_method
- alias :define_nullify_dependency_method :define_destroy_dependency_method
-
- def dependency_method_name
- "has_one_dependent_#{options[:dependent]}_for_#{name}"
- end
+ def valid_dependent_options
+ [:destroy, :delete, :nullify, :restrict, :restrict_with_error, :restrict_with_exception]
+ end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb
index 90a4b7c2ef..6a5830e57f 100644
--- a/activerecord/lib/active_record/associations/builder/singular_association.rb
+++ b/activerecord/lib/active_record/associations/builder/singular_association.rb
@@ -13,22 +13,20 @@ module ActiveRecord::Associations::Builder
define_constructors if constructable?
end
- private
-
- def define_constructors
- name = self.name
-
- mixin.redefine_method("build_#{name}") do |*params, &block|
- association(name).build(*params, &block)
+ def define_constructors
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
+ def build_#{name}(*args, &block)
+ association(:#{name}).build(*args, &block)
end
- mixin.redefine_method("create_#{name}") do |*params, &block|
- association(name).create(*params, &block)
+ def create_#{name}(*args, &block)
+ association(:#{name}).create(*args, &block)
end
- mixin.redefine_method("create_#{name}!") do |*params, &block|
- association(name).create!(*params, &block)
+ def create_#{name}!(*args, &block)
+ association(:#{name}).create!(*args, &block)
end
- end
+ CODE
+ end
end
end
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index f8c1103ea9..b15df4f308 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -270,12 +270,20 @@ module ActiveRecord
load_target.size
end
- # Returns true if the collection is empty. Equivalent to
- # <tt>collection.size.zero?</tt>. If the collection has not been already
+ # Returns true if the collection is empty.
+ #
+ # If the collection has been loaded or the <tt>:counter_sql</tt> option
+ # is provided, it is equivalent to <tt>collection.size.zero?</tt>. If the
+ # collection has not been loaded, it is equivalent to
+ # <tt>collection.exists?</tt>. If the collection has not already been
# loaded and you are going to fetch the records anyway it is better to
# check <tt>collection.length.zero?</tt>.
def empty?
- size.zero?
+ if loaded? || options[:counter_sql]
+ size.zero?
+ else
+ !scope.exists?
+ end
end
# Returns true if the collections is not empty.
@@ -566,7 +574,7 @@ module ActiveRecord
args.shift if args.first.is_a?(Hash) && args.first.empty?
collection = fetch_first_or_last_using_find?(args) ? scope : load_target
- collection.send(type, *args)
+ collection.send(type, *args).tap {|it| set_inverse_instance it }
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 7a363c896e..74864d271f 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -7,6 +7,28 @@ module ActiveRecord
# is provided by its child HasManyThroughAssociation.
class HasManyAssociation < CollectionAssociation #:nodoc:
+ def handle_dependency
+ case options[:dependent]
+ when :restrict, :restrict_with_exception
+ raise ActiveRecord::DeleteRestrictionError.new(reflection.name) unless empty?
+
+ when :restrict_with_error
+ unless empty?
+ record = klass.human_attribute_name(reflection.name).downcase
+ owner.errors.add(:base, :"restrict_dependent_destroy.many", record: record)
+ false
+ end
+
+ else
+ if options[:dependent] == :destroy
+ # No point in executing the counter update since we're going to destroy the parent anyway
+ load_target.each(&:mark_for_destruction)
+ end
+
+ delete_all
+ end
+ end
+
def insert_record(record, validate = true, raise = false)
set_owner_attributes(record)
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index c5aed6e26a..dd7da59a86 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -3,23 +3,43 @@ module ActiveRecord
# = Active Record Belongs To Has One Association
module Associations
class HasOneAssociation < SingularAssociation #:nodoc:
- def replace(record, save = true)
- raise_on_type_mismatch(record) if record
- load_target
- reflection.klass.transaction do
- if target && target != record
- remove_target!(options[:dependent]) unless target.destroyed?
+ def handle_dependency
+ case options[:dependent]
+ when :restrict, :restrict_with_exception
+ raise ActiveRecord::DeleteRestrictionError.new(reflection.name) if load_target
+
+ when :restrict_with_error
+ if load_target
+ record = klass.human_attribute_name(reflection.name).downcase
+ owner.errors.add(:base, :"restrict_dependent_destroy.one", record: record)
+ false
end
- if record
- set_owner_attributes(record)
- set_inverse_instance(record)
+ else
+ delete
+ end
+ end
+
+ def replace(record, save = true)
+ raise_on_type_mismatch(record) if record
+ load_target
- if owner.persisted? && save && !record.save
- nullify_owner_attributes(record)
- set_owner_attributes(target) if target
- raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
+ # 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
+ remove_target!(options[:dependent]) if target && !target.destroyed?
+
+ if record
+ set_owner_attributes(record)
+ set_inverse_instance(record)
+
+ if owner.persisted? && save && !record.save
+ nullify_owner_attributes(record)
+ set_owner_attributes(target) if target
+ raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
+ end
end
end
end
@@ -51,16 +71,19 @@ module ActiveRecord
end
def remove_target!(method)
- if method.in?([:delete, :destroy])
- target.send(method)
- else
- nullify_owner_attributes(target)
+ case method
+ when :delete
+ target.delete
+ when :destroy
+ target.destroy
+ else
+ nullify_owner_attributes(target)
- if target.persisted? && owner.persisted? && !target.save
- set_owner_attributes(target)
- raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
- "The record failed to save when after its foreign key was set to nil."
- end
+ if target.persisted? && owner.persisted? && !target.save
+ set_owner_attributes(target)
+ raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
+ "The record failed to save when after its foreign key was set to nil."
+ end
end
end
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index 6992840040..d9989274c8 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -84,11 +84,11 @@ module ActiveRecord
def assign_attributes(new_attributes, options = {})
return if new_attributes.blank?
- attributes = new_attributes.stringify_keys
- multi_parameter_attributes = []
+ attributes = new_attributes.stringify_keys
+ multi_parameter_attributes = []
nested_parameter_attributes = []
- previous_options = @mass_assignment_options
- @mass_assignment_options = options
+ previous_options = @mass_assignment_options
+ @mass_assignment_options = options
unless options[:without_protection]
attributes = sanitize_for_mass_assignment(attributes, mass_assignment_role)
@@ -97,23 +97,15 @@ module ActiveRecord
attributes.each do |k, v|
if k.include?("(")
multi_parameter_attributes << [ k, v ]
- elsif respond_to?("#{k}=")
- if v.is_a?(Hash)
- nested_parameter_attributes << [ k, v ]
- else
- send("#{k}=", v)
- end
+ elsif v.is_a?(Hash)
+ nested_parameter_attributes << [ k, v ]
else
- raise(UnknownAttributeError, "unknown attribute: #{k}")
+ _assign_attribute(k, v)
end
end
- # assign any deferred nested attributes after the base attributes have been set
- nested_parameter_attributes.each do |k,v|
- send("#{k}=", v)
- end
-
- assign_multiparameter_attributes(multi_parameter_attributes)
+ assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
+ assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
ensure
@mass_assignment_options = previous_options
end
@@ -130,6 +122,21 @@ module ActiveRecord
private
+ def _assign_attribute(k, v)
+ public_send("#{k}=", v)
+ rescue NoMethodError
+ if respond_to?("#{k}=")
+ raise
+ else
+ raise UnknownAttributeError, "unknown attribute: #{k}"
+ end
+ end
+
+ # Assign any deferred nested attributes after the base attributes have been set.
+ def assign_nested_parameter_attributes(pairs)
+ pairs.each { |k, v| _assign_attribute(k, v) }
+ end
+
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
@@ -143,19 +150,11 @@ module ActiveRecord
)
end
- def instantiate_time_object(name, values)
- if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
- Time.zone.local(*values)
- else
- Time.time_with_datetime_fallback(self.class.default_timezone, *values)
- end
- end
-
def execute_callstack_for_multiparameter_attributes(callstack)
errors = []
callstack.each do |name, values_with_empty_parameters|
begin
- send(name + "=", read_value_from_parameter(name, values_with_empty_parameters))
+ send("#{name}=", MultiparameterAttribute.new(self, name, values_with_empty_parameters).read_value)
rescue => ex
errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
end
@@ -166,74 +165,12 @@ module ActiveRecord
end
end
- def read_value_from_parameter(name, values_hash_from_param)
- klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
- if values_hash_from_param.values.all?{|v|v.nil?}
- nil
- elsif klass == Time
- read_time_parameter_value(name, values_hash_from_param)
- elsif klass == Date
- read_date_parameter_value(name, values_hash_from_param)
- else
- read_other_parameter_value(klass, name, values_hash_from_param)
- end
- end
-
- def read_time_parameter_value(name, values_hash_from_param)
- # If column is a :time (and not :date or :timestamp) there is no need to validate if
- # there are year/month/day fields
- if column_for_attribute(name).type == :time
- # if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil
- {1 => 1970, 2 => 1, 3 => 1}.each do |key,value|
- values_hash_from_param[key] ||= value
- end
- else
- # else column is a timestamp, so if Date bits were not provided, error
- if missing_parameter = [1,2,3].detect{ |position| !values_hash_from_param.has_key?(position) }
- raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter}i)")
- end
-
- # If Date bits were provided but blank, then return nil
- return nil if (1..3).any? { |position| values_hash_from_param[position].blank? }
- end
-
- max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
- set_values = (1..max_position).collect{ |position| values_hash_from_param[position] }
- # If Time bits are not there, then default to 0
- (3..5).each { |i| set_values[i] = set_values[i].blank? ? 0 : set_values[i] }
- instantiate_time_object(name, set_values)
- end
-
- def read_date_parameter_value(name, values_hash_from_param)
- return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
- set_values = [values_hash_from_param[1], values_hash_from_param[2], values_hash_from_param[3]]
- begin
- Date.new(*set_values)
- rescue ArgumentError # if Date.new raises an exception on an invalid date
- instantiate_time_object(name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
- end
- end
-
- def read_other_parameter_value(klass, name, values_hash_from_param)
- max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param)
- values = (1..max_position).collect do |position|
- raise "Missing Parameter" if !values_hash_from_param.has_key?(position)
- values_hash_from_param[position]
- end
- klass.new(*values)
- end
-
- def extract_max_param_for_multiparameter_attributes(values_hash_from_param, upper_cap = 100)
- [values_hash_from_param.keys.max,upper_cap].min
- end
-
def extract_callstack_for_multiparameter_attributes(pairs)
attributes = { }
- pairs.each do |pair|
- multiparameter_name, value = pair
+ pairs.each do |(multiparameter_name, value)|
attribute_name = multiparameter_name.split("(").first
- attributes[attribute_name] = {} unless attributes.include?(attribute_name)
+ attributes[attribute_name] ||= {}
parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
@@ -250,5 +187,100 @@ module ActiveRecord
multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
end
+ class MultiparameterAttribute #:nodoc:
+ attr_reader :object, :name, :values, :column
+
+ def initialize(object, name, values)
+ @object = object
+ @name = name
+ @values = values
+ end
+
+ def read_value
+ return if values.values.compact.empty?
+
+ @column = object.class.reflect_on_aggregation(name.to_sym) || object.column_for_attribute(name)
+ klass = column.klass
+
+ if klass == Time
+ read_time
+ elsif klass == Date
+ read_date
+ else
+ read_other(klass)
+ end
+ end
+
+ private
+
+ def instantiate_time_object(set_values)
+ if object.class.send(:create_time_zone_conversion_attribute?, name, column)
+ Time.zone.local(*set_values)
+ else
+ Time.time_with_datetime_fallback(object.class.default_timezone, *set_values)
+ end
+ end
+
+ def read_time
+ # If column is a :time (and not :date or :timestamp) there is no need to validate if
+ # there are year/month/day fields
+ if column.type == :time
+ # if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil
+ { 1 => 1970, 2 => 1, 3 => 1 }.each do |key,value|
+ values[key] ||= value
+ end
+ else
+ # else column is a timestamp, so if Date bits were not provided, error
+ validate_missing_parameters!([1,2,3])
+
+ # If Date bits were provided but blank, then return nil
+ return if blank_date_parameter?
+ end
+
+ max_position = extract_max_param(6)
+ set_values = values.values_at(*(1..max_position))
+ # If Time bits are not there, then default to 0
+ (3..5).each { |i| set_values[i] = set_values[i].presence || 0 }
+ instantiate_time_object(set_values)
+ end
+
+ def read_date
+ return if blank_date_parameter?
+ set_values = values.values_at(1,2,3)
+ begin
+ Date.new(*set_values)
+ rescue ArgumentError # if Date.new raises an exception on an invalid date
+ instantiate_time_object(set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
+ end
+ end
+
+ def read_other(klass)
+ max_position = extract_max_param
+ positions = (1..max_position)
+ validate_missing_parameters!(positions)
+
+ set_values = values.values_at(*positions)
+ klass.new(*set_values)
+ end
+
+ # Checks whether some blank date parameter exists. Note that this is different
+ # than the validate_missing_parameters! method, since it just checks for blank
+ # positions instead of missing ones, and does not raise in case one blank position
+ # exists. The caller is responsible to handle the case of this returning true.
+ def blank_date_parameter?
+ (1..3).any? { |position| values[position].blank? }
+ end
+
+ # If some position is not provided, it errors out a missing parameter exception.
+ def validate_missing_parameters!(positions)
+ if missing_parameter = positions.detect { |position| !values.key?(position) }
+ raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter})")
+ end
+ end
+
+ def extract_max_param(upper_cap = 100)
+ [values.keys.max, upper_cap].min
+ end
+ end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index a7af086e43..5fd139156c 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -8,7 +8,6 @@ module ActiveRecord
extend ActiveSupport::Concern
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
- ActiveRecord::Model.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
included do
config_attribute :attribute_types_cached_by_default
@@ -64,6 +63,8 @@ module ActiveRecord
end
end
+ ActiveRecord::Model.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
+
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name)
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index 49ab3ab808..bdda5bc009 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -6,10 +6,46 @@ module ActiveRecord
included do
# Returns a hash of all the attributes that have been specified for serialization as
# keys and their class restriction as values.
- class_attribute :serialized_attributes, instance_writer: false
+ class_attribute :serialized_attributes, instance_accessor: false
self.serialized_attributes = {}
end
+ module ClassMethods
+ # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
+ # then specify the name of that attribute using this method and it will be handled automatically.
+ # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
+ # class on retrieval or SerializationTypeMismatch will be raised.
+ #
+ # ==== Parameters
+ #
+ # * +attr_name+ - The field name that should be serialized.
+ # * +class_name+ - Optional, class name that the object type should be equal to.
+ #
+ # ==== Example
+ # # Serialize a preferences attribute
+ # class User < ActiveRecord::Base
+ # serialize :preferences
+ # end
+ def serialize(attr_name, class_name = Object)
+ include Behavior
+
+ coder = if [:load, :dump].all? { |x| class_name.respond_to?(x) }
+ class_name
+ else
+ Coders::YAMLColumn.new(class_name)
+ end
+
+ # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy
+ # has its own hash of own serialized attributes
+ self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
+ end
+ end
+
+ def serialized_attributes
+ ActiveSupport::Deprecation.warn("Instance level serialized_attributes method is deprecated, please use class level method.")
+ defined?(@serialized_attributes) ? @serialized_attributes : self.class.serialized_attributes
+ end
+
class Type # :nodoc:
def initialize(column)
@column = column
@@ -44,71 +80,50 @@ module ActiveRecord
end
end
- module ClassMethods
- # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
- # then specify the name of that attribute using this method and it will be handled automatically.
- # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
- # class on retrieval or SerializationTypeMismatch will be raised.
- #
- # ==== Parameters
- #
- # * +attr_name+ - The field name that should be serialized.
- # * +class_name+ - Optional, class name that the object type should be equal to.
- #
- # ==== Example
- # # Serialize a preferences attribute
- # class User < ActiveRecord::Base
- # serialize :preferences
- # end
- def serialize(attr_name, class_name = Object)
- coder = if [:load, :dump].all? { |x| class_name.respond_to?(x) }
- class_name
- else
- Coders::YAMLColumn.new(class_name)
- end
+ # This is only added to the model when serialize is called, which
+ # ensures we do not make things slower when serialization is not used.
+ module Behavior #:nodoc:
+ extend ActiveSupport::Concern
- # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy
- # has its own hash of own serialized attributes
- self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
- end
-
- def initialize_attributes(attributes, options = {}) #:nodoc:
- serialized = (options.delete(:serialized) { true }) ? :serialized : :unserialized
- super(attributes, options)
+ module ClassMethods
+ def initialize_attributes(attributes, options = {})
+ serialized = (options.delete(:serialized) { true }) ? :serialized : :unserialized
+ super(attributes, options)
- serialized_attributes.each do |key, coder|
- if attributes.key?(key)
- attributes[key] = Attribute.new(coder, attributes[key], serialized)
+ serialized_attributes.each do |key, coder|
+ if attributes.key?(key)
+ attributes[key] = Attribute.new(coder, attributes[key], serialized)
+ end
end
+
+ attributes
end
- attributes
- end
+ private
- private
+ def attribute_cast_code(attr_name)
+ if serialized_attributes.include?(attr_name)
+ "v.unserialized_value"
+ else
+ super
+ end
+ end
+ end
- def attribute_cast_code(attr_name)
- if serialized_attributes.include?(attr_name)
- "v.unserialized_value"
+ def type_cast_attribute_for_write(column, value)
+ if column && coder = self.class.serialized_attributes[column.name]
+ Attribute.new(coder, value, :unserialized)
else
super
end
end
- end
-
- def type_cast_attribute_for_write(column, value)
- if column && coder = self.class.serialized_attributes[column.name]
- Attribute.new(coder, value, :unserialized)
- else
- super
- end
- end
- def read_attribute_before_type_cast(attr_name)
- if serialized_attributes.include?(attr_name)
- super.unserialized_value
- else
- super
+ def read_attribute_before_type_cast(attr_name)
+ if self.class.serialized_attributes.include?(attr_name)
+ super.unserialized_value
+ else
+ super
+ 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 fa5b2ef336..d1e9d2de0e 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -59,11 +59,14 @@ module ActiveRecord
unless time.acts_like?(:time)
time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
end
- time = time.in_time_zone rescue nil if time
- changed = read_attribute(:#{attr_name}) != time
- write_attribute(:#{attr_name}, original_time)
- #{attr_name}_will_change! if changed
- @attributes_cache["#{attr_name}"] = time
+ 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
end
EOV
generated_attribute_methods.module_eval(method_body, __FILE__, line)
@@ -79,6 +82,12 @@ module ActiveRecord
[:datetime, :timestamp].include?(column.type)
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/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index b0b51f540c..4e1f0e1d62 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -1,6 +1,12 @@
module ActiveRecord
module ConnectionAdapters # :nodoc:
module DatabaseStatements
+ def initialize
+ super
+ @_current_transaction_records = []
+ @transaction_joinable = nil
+ end
+
# Converts an arel AST to SQL
def to_sql(arel, binds = [])
if arel.respond_to?(:ast)
@@ -167,31 +173,23 @@ module ActiveRecord
def transaction(options = {})
options.assert_valid_keys :requires_new, :joinable
- last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil
- if options.has_key?(:joinable)
- @transaction_joinable = options[:joinable]
- else
- @transaction_joinable = true
- end
- requires_new = options[:requires_new] || !last_transaction_joinable
-
- transaction_open = false
- @_current_transaction_records ||= []
+ last_transaction_joinable = @transaction_joinable
+ @transaction_joinable = options.fetch(:joinable, true)
+ requires_new = options[:requires_new] || !last_transaction_joinable
+ transaction_open = false
begin
- if block_given?
- if requires_new || open_transactions == 0
- if open_transactions == 0
- begin_db_transaction
- elsif requires_new
- create_savepoint
- end
- increment_open_transactions
- transaction_open = true
- @_current_transaction_records.push([])
+ if requires_new || open_transactions == 0
+ if open_transactions == 0
+ begin_db_transaction
+ elsif requires_new
+ create_savepoint
end
- yield
+ increment_open_transactions
+ transaction_open = true
+ @_current_transaction_records.push([])
end
+ yield
rescue Exception => database_transaction_rollback
if transaction_open && !outside_transaction?
transaction_open = false
@@ -225,7 +223,7 @@ module ActiveRecord
@_current_transaction_records.last.concat(save_point_records)
end
end
- rescue Exception => database_transaction_rollback
+ rescue Exception
if open_transactions == 0
rollback_db_transaction
rollback_transaction_records(true)
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index b9045cf1e7..1445bb3b2f 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -208,7 +208,7 @@ module ActiveRecord
# '0.123456' -> 123456
# '1.123456' -> 123456
def microseconds(time)
- ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
+ time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0
end
def new_date(year, mon, mday)
@@ -233,7 +233,7 @@ module ActiveRecord
# Doesn't handle time zones.
def fast_string_to_time(string)
if string =~ Format::ISO_DATETIME
- microsec = ($7.to_f * 1_000_000).to_i
+ microsec = ($7.to_r * 1_000_000).to_i
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
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 3b0353358a..6bf7af081f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -298,13 +298,6 @@ module ActiveRecord
@connection.insert_id
end
- class Result < ActiveRecord::Result
- def initialize(columns, rows, column_types)
- super(columns, rows)
- @column_types = column_types
- end
- end
-
module Fields
class Type
def type; end
@@ -437,7 +430,7 @@ module ActiveRecord
}
end
}
- result_set = Result.new(types.keys, result.to_a, types)
+ result_set = ActiveRecord::Result.new(types.keys, result.to_a, types)
result.free
else
result_set = ActiveRecord::Result.new([], [])
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 8e9ce80697..40cd65cce9 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -803,13 +803,6 @@ module ActiveRecord
Arel::Nodes::BindParam.new "$#{index + 1}"
end
- class Result < ActiveRecord::Result
- def initialize(columns, rows, column_types)
- super(columns, rows)
- @column_types = column_types
- end
- end
-
def exec_query(sql, name = 'SQL', binds = [])
log(sql, name, binds) do
result = binds.empty? ? exec_no_cache(sql, binds) :
@@ -825,7 +818,7 @@ module ActiveRecord
}
end
- ret = Result.new(result.fields, result.values, types)
+ ret = ActiveRecord::Result.new(result.fields, result.values, types)
result.clear
return ret
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 57aa47ab61..4fe0013f0f 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -380,9 +380,9 @@ module ActiveRecord
case field["dflt_value"]
when /^null$/i
field["dflt_value"] = nil
- when /^'(.*)'$/
+ when /^'(.*)'$/m
field["dflt_value"] = $1.gsub("''", "'")
- when /^"(.*)"$/
+ when /^"(.*)"$/m
field["dflt_value"] = $1.gsub('""', '"')
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 1145d2138c..aad21b8e37 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -1,5 +1,5 @@
require 'active_support/core_ext/hash/indifferent_access'
-require 'active_support/core_ext/object/deep_dup'
+require 'active_support/core_ext/object/duplicable'
require 'thread'
module ActiveRecord
@@ -82,15 +82,6 @@ module ActiveRecord
# The connection handler
config_attribute :connection_handler
- ##
- # :singleton-method:
- # Specifies whether or not has_many or has_one association option
- # :dependent => :restrict raises an exception. If set to true, the
- # ActiveRecord::DeleteRestrictionError exception will be raised
- # along with a DEPRECATION WARNING. If set to false, an error would
- # be added to the model instead.
- config_attribute :dependent_restrict_raises
-
%w(logger configurations default_timezone schema_format timestamped_migrations).each do |name|
config_attribute name, global: true
end
@@ -182,7 +173,10 @@ module ActiveRecord
# # 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.deep_dup)
+ defaults = self.class.column_defaults.dup
+ defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? }
+
+ @attributes = self.class.initialize_attributes(defaults)
@columns_hash = self.class.column_types.dup
init_internals
@@ -194,7 +188,7 @@ module ActiveRecord
assign_attributes(attributes, options) if attributes
yield self if block_given?
- run_callbacks :initialize if _initialize_callbacks.any?
+ run_callbacks :initialize unless _initialize_callbacks.empty?
end
# Initialize an empty model object from +coder+. +coder+ must contain
@@ -399,6 +393,7 @@ module ActiveRecord
@marked_for_destruction = false
@new_record = true
@mass_assignment_options = nil
+ @_start_transaction_state = {}
end
end
end
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index b27a19f89a..c877079b25 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -25,7 +25,7 @@ module ActiveRecord
foreign_key = has_many_association.foreign_key.to_s
child_class = has_many_association.klass
belongs_to = child_class.reflect_on_all_associations(:belongs_to)
- reflection = belongs_to.find { |e| e.foreign_key.to_s == foreign_key }
+ reflection = belongs_to.find { |e| e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
counter_name = reflection.counter_cache_column
stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb
index 843587c32e..3bac31c6aa 100644
--- a/activerecord/lib/active_record/dynamic_matchers.rb
+++ b/activerecord/lib/active_record/dynamic_matchers.rb
@@ -1,8 +1,8 @@
module ActiveRecord
module DynamicMatchers #:nodoc:
# This code in this file seems to have a lot of indirection, but the indirection
- # is there to provide extension points for the active_record_deprecated_finders
- # gem. When we stop supporting active_record_deprecated_finders (from Rails 5),
+ # is there to provide extension points for the activerecord-deprecated_finders
+ # gem. When we stop supporting activerecord-deprecated_finders (from Rails 5),
# then we can remove the indirection.
def respond_to?(name, include_private = false)
@@ -74,17 +74,17 @@ module ActiveRecord
end
module Finder
- # Extended in active_record_deprecated_finders
+ # Extended in activerecord-deprecated_finders
def body
result
end
- # Extended in active_record_deprecated_finders
+ # Extended in activerecord-deprecated_finders
def result
"#{finder}(#{attributes_hash})"
end
- # Extended in active_record_deprecated_finders
+ # Extended in activerecord-deprecated_finders
def signature
attribute_names.join(', ')
end
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 7d759c1048..04fff99a6e 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -55,7 +55,7 @@ module ActiveRecord
end
sup = active_record_super
- if sup.in?([Base, Model]) || sup.abstract_class?
+ if sup == Base || sup == Model || sup.abstract_class?
self
else
sup.base_class
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index e0344f3c56..e96ed00f9c 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -168,16 +168,16 @@ module ActiveRecord
super
end
- # If the locking column has no default value set,
- # start the lock version at zero. Note we can't use
- # <tt>locking_enabled?</tt> at this point as
- # <tt>@attributes</tt> may not have been initialized yet.
- def initialize_attributes(attributes, options = {}) #:nodoc:
- if attributes.key?(locking_column) && lock_optimistically
- attributes[locking_column] ||= 0
- end
+ def column_defaults
+ @column_defaults ||= begin
+ defaults = super
+
+ if defaults.key?(locking_column) && lock_optimistically
+ defaults[locking_column] ||= 0
+ end
- attributes
+ defaults
+ end
end
end
end
diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb
index a326dabcd3..57553c29eb 100644
--- a/activerecord/lib/active_record/model.rb
+++ b/activerecord/lib/active_record/model.rb
@@ -141,7 +141,7 @@ module ActiveRecord
"ActiveSupport::Concern + include, which will ensure that your class methods are " \
"inherited."
)
- @base.extend *mods
+ @base.extend(*mods)
end
end
end
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index def48c03bf..99de16cd33 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -225,7 +225,7 @@ module ActiveRecord
def decorate_columns(columns_hash) # :nodoc:
return if columns_hash.empty?
- serialized_attributes.keys.each do |key|
+ serialized_attributes.each_key do |key|
columns_hash[key] = AttributeMethods::Serialization::Type.new(columns_hash[key])
end
@@ -259,13 +259,12 @@ module ActiveRecord
# and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute
# is available.
def column_methods_hash #:nodoc:
- @dynamic_methods_hash ||= column_names.inject(Hash.new(false)) do |methods, attr|
+ @dynamic_methods_hash ||= column_names.each_with_object(Hash.new(false)) do |attr, methods|
attr_name = attr.to_s
methods[attr.to_sym] = attr_name
methods["#{attr}=".to_sym] = attr_name
methods["#{attr}?".to_sym] = attr_name
methods["#{attr}_before_type_cast".to_sym] = attr_name
- methods
end
end
@@ -312,13 +311,19 @@ module ActiveRecord
@relation = nil
end
+ # This is a hook for use by modules that need to do extra stuff to
+ # attributes when they are initialized. (e.g. attribute
+ # serialization)
+ def initialize_attributes(attributes, options = {}) #:nodoc:
+ attributes
+ end
+
private
# Guesses the table name, but does not decorate it with prefix and suffix information.
def undecorated_table_name(class_name = base_class.name)
table_name = class_name.to_s.demodulize.underscore
- table_name = table_name.pluralize if pluralize_table_names
- table_name
+ pluralize_table_names ? table_name.pluralize : table_name
end
# Computes and returns a table name according to default conventions.
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index be013a068c..f91e41b535 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -245,7 +245,8 @@ module ActiveRecord
# any value for _destroy.
# [:limit]
# Allows you to specify the maximum number of the associated records that
- # can be processed with the nested attributes. If the size of the
+ # can be processed with the nested attributes. Limit also can be specified as a
+ # Proc or a Symbol pointing to a method that should return number. If the size of the
# nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords
# exception is raised. If omitted, any number associations can be processed.
# Note that the :limit option is only applicable to one-to-many associations.
@@ -388,8 +389,17 @@ module ActiveRecord
raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
end
- if options[:limit] && attributes_collection.size > options[:limit]
- raise TooManyRecords, "Maximum #{options[:limit]} records are allowed. Got #{attributes_collection.size} records instead."
+ limit = case options[:limit]
+ when Symbol
+ send(options[:limit])
+ when Proc
+ options[:limit].call
+ else
+ options[:limit]
+ end
+
+ if limit && attributes_collection.size > limit
+ raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead."
end
if attributes_collection.is_a? Hash
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 593fed5a85..6b4b9bd103 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -218,7 +218,7 @@ module ActiveRecord
raise ActiveRecordError, "can not update on a new record object" unless persisted?
attributes.each_key do |key|
- raise ActiveRecordError, "#{key.to_s} is marked as readonly" if self.class.readonly_attributes.include?(key.to_s)
+ raise ActiveRecordError, "#{key} is marked as readonly" if self.class.readonly_attributes.include?(key.to_s)
end
attributes.each do |k,v|
@@ -391,9 +391,5 @@ module ActiveRecord
@new_record = false
id
end
-
- def verify_readonly_attribute(name)
- raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
- end
end
end
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 672d9a4246..ecf8547e67 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -29,8 +29,11 @@ module ActiveRecord
'ActiveRecord::RecordNotSaved' => :unprocessable_entity
)
+
config.active_record.use_schema_cache_dump = true
+ config.eager_load_namespaces << ActiveRecord
+
rake_tasks do
require "active_record/base"
load "active_record/railties/databases.rake"
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 6bb0c39b79..ae24542521 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/object/inclusion'
require 'active_record'
db_namespace = namespace :db do
@@ -242,7 +241,7 @@ db_namespace = namespace :db do
end
end
- task :load_if_ruby => 'db:create' do
+ task :load_if_ruby => [:environment, 'db:create'] do
db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby
end
@@ -327,7 +326,7 @@ db_namespace = namespace :db do
end
end
- task :load_if_sql => 'db:create' do
+ task :load_if_sql => [:environment, 'db:create'] do
db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql
end
end
@@ -409,21 +408,6 @@ db_namespace = namespace :db do
end
end
end
-
- namespace :sessions do
- # desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
- task :create => [:environment, :load_config] do
- raise 'Task unavailable to this database (no migration support)' unless ActiveRecord::Base.connection.supports_migrations?
- Rails.application.load_generators
- require 'rails/generators/rails/session_migration/session_migration_generator'
- Rails::Generators::SessionMigrationGenerator.start [ ENV['MIGRATION'] || 'add_sessions_table' ]
- end
-
- # desc "Clear the sessions table"
- task :clear => [:environment, :load_config] do
- ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::SessionStore::Session.table_name}"
- end
- end
end
namespace :railties do
diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb
index 1d8c566e40..b3c20c4aff 100644
--- a/activerecord/lib/active_record/readonly_attributes.rb
+++ b/activerecord/lib/active_record/readonly_attributes.rb
@@ -4,7 +4,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :_attr_readonly, instance_writer: false
+ class_attribute :_attr_readonly, instance_accessor: false
self._attr_readonly = []
end
@@ -20,5 +20,10 @@ module ActiveRecord
self._attr_readonly
end
end
+
+ def _attr_readonly
+ ActiveSupport::Deprecation.warn("Instance level _attr_readonly method is deprecated, please use class level method.")
+ defined?(@_attr_readonly) ? @_attr_readonly : self.class._attr_readonly
+ end
end
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 9ed3256ae9..2d0457636e 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -18,6 +18,7 @@ module ActiveRecord
attr_reader :table, :klass, :loaded
attr_accessor :default_scoped
+ alias :model :klass
alias :loaded? :loaded
alias :default_scoped? :default_scoped
@@ -258,6 +259,8 @@ module ActiveRecord
# # Update all books that match conditions, but limit it to 5 ordered by date
# Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(:author => 'David')
def update_all(updates)
+ raise ArgumentError, "Empty list of attributes to change" if updates.blank?
+
stmt = Arel::UpdateManager.new(arel.engine)
stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
@@ -466,7 +469,7 @@ module ActiveRecord
# Returns sql statement for the relation.
#
# Users.where(name: 'Oscar').to_sql
- # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
+ # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
def to_sql
@to_sql ||= klass.connection.to_sql(arel, bind_values.dup)
end
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index 71aaedee1e..e5b50673da 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -97,15 +97,13 @@ module ActiveRecord
merged_wheres = relation.where_values + values[:where]
unless relation.where_values.empty?
- # Remove duplicates, last one wins.
- seen = Hash.new { |h,table| h[table] = {} }
+ # Remove equalities with duplicated left-hand. Last one wins.
+ seen = {}
merged_wheres = merged_wheres.reverse.reject { |w|
nuke = false
if w.respond_to?(:operator) && w.operator == :==
- name = w.left.name
- table = w.left.relation.name
- nuke = seen[table][name]
- seen[table][name] = true
+ nuke = seen[w.left]
+ seen[w.left] = true
end
nuke
}.reverse
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 8e6254f918..f6bacf4822 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -136,8 +136,8 @@ module ActiveRecord
# Second: Modifies the SELECT statement for the query so that only certain
# fields are retrieved:
#
- # >> Model.select(:field)
- # => [#<Model field:value>]
+ # Model.select(:field)
+ # # => [#<Model field:value>]
#
# Although in the above example it looks as though this method returns an
# array, it actually returns a relation object and can have other query
@@ -145,25 +145,26 @@ module ActiveRecord
#
# The argument to the method can also be an array of fields.
#
- # >> Model.select([:field, :other_field, :and_one_more])
- # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
+ # Model.select(:field, :other_field, :and_one_more)
+ # # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
#
# Accessing attributes of an object that do not have fields retrieved by a select
# will throw <tt>ActiveModel::MissingAttributeError</tt>:
#
- # >> Model.select(:field).first.other_field
- # => ActiveModel::MissingAttributeError: missing attribute: other_field
- def select(value = Proc.new)
+ # Model.select(:field).first.other_field
+ # # => ActiveModel::MissingAttributeError: missing attribute: other_field
+ def select(*fields)
if block_given?
- to_a.select { |*block_args| value.call(*block_args) }
+ to_a.select { |*block_args| yield(*block_args) }
else
- spawn.select!(value)
+ raise ArgumentError, 'Call this with at least one field' if fields.empty?
+ spawn.select!(*fields)
end
end
# Like #select, but modifies relation in place.
- def select!(value)
- self.select_values += Array.wrap(value)
+ def select!(*fields)
+ self.select_values += fields.flatten
self
end
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index fd276ccf5d..2414a4bbd7 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -10,11 +10,11 @@ module ActiveRecord
attr_reader :columns, :rows, :column_types
- def initialize(columns, rows)
+ def initialize(columns, rows, column_types = {})
@columns = columns
@rows = rows
@hash_rows = nil
- @column_types = {}
+ @column_types = column_types
end
def each
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
deleted file mode 100644
index 58e1dab508..0000000000
--- a/activerecord/lib/active_record/session_store.rb
+++ /dev/null
@@ -1,365 +0,0 @@
-require 'action_dispatch/middleware/session/abstract_store'
-
-module ActiveRecord
- # = Active Record Session Store
- #
- # A session store backed by an Active Record class. A default class is
- # provided, but any object duck-typing to an Active Record Session class
- # with text +session_id+ and +data+ attributes is sufficient.
- #
- # The default assumes a +sessions+ tables with columns:
- # +id+ (numeric primary key),
- # +session_id+ (string, usually varchar; maximum length is 255), and
- # +data+ (text or longtext; careful if your session data exceeds 65KB).
- #
- # The +session_id+ column should always be indexed for speedy lookups.
- # Session data is marshaled to the +data+ column in Base64 format.
- # If the data you write is larger than the column's size limit,
- # ActionController::SessionOverflowError will be raised.
- #
- # You may configure the table name, primary key, and data column.
- # For example, at the end of <tt>config/application.rb</tt>:
- #
- # ActiveRecord::SessionStore::Session.table_name = 'legacy_session_table'
- # ActiveRecord::SessionStore::Session.primary_key = 'session_id'
- # ActiveRecord::SessionStore::Session.data_column_name = 'legacy_session_data'
- #
- # Note that setting the primary key to the +session_id+ frees you from
- # having a separate +id+ column if you don't want it. However, you must
- # set <tt>session.model.id = session.session_id</tt> by hand! A before filter
- # on ApplicationController is a good place.
- #
- # Since the default class is a simple Active Record, you get timestamps
- # for free if you add +created_at+ and +updated_at+ datetime columns to
- # the +sessions+ table, making periodic session expiration a snap.
- #
- # You may provide your own session class implementation, whether a
- # feature-packed Active Record or a bare-metal high-performance SQL
- # store, by setting
- #
- # ActiveRecord::SessionStore.session_class = MySessionClass
- #
- # You must implement these methods:
- #
- # self.find_by_session_id(session_id)
- # initialize(hash_of_session_id_and_data, options_hash = {})
- # attr_reader :session_id
- # attr_accessor :data
- # save
- # destroy
- #
- # The example SqlBypass class is a generic SQL session store. You may
- # use it as a basis for high-performance database-specific stores.
- class SessionStore < ActionDispatch::Session::AbstractStore
- module ClassMethods # :nodoc:
- def marshal(data)
- ::Base64.encode64(Marshal.dump(data)) if data
- end
-
- def unmarshal(data)
- Marshal.load(::Base64.decode64(data)) if data
- end
-
- def drop_table!
- connection.schema_cache.clear_table_cache!(table_name)
- connection.drop_table table_name
- end
-
- def create_table!
- connection.schema_cache.clear_table_cache!(table_name)
- connection.create_table(table_name) do |t|
- t.string session_id_column, :limit => 255
- t.text data_column_name
- end
- connection.add_index table_name, session_id_column, :unique => true
- end
- end
-
- # The default Active Record class.
- class Session < ActiveRecord::Base
- extend ClassMethods
-
- ##
- # :singleton-method:
- # Customizable data column name. Defaults to 'data'.
- cattr_accessor :data_column_name
- self.data_column_name = 'data'
-
- attr_accessible :session_id, :data, :marshaled_data
-
- before_save :marshal_data!
- before_save :raise_on_session_data_overflow!
-
- class << self
- def data_column_size_limit
- @data_column_size_limit ||= columns_hash[data_column_name].limit
- end
-
- # Hook to set up sessid compatibility.
- def find_by_session_id(session_id)
- setup_sessid_compatibility!
- find_by_session_id(session_id)
- end
-
- private
- def session_id_column
- 'session_id'
- end
-
- # Compatibility with tables using sessid instead of session_id.
- def setup_sessid_compatibility!
- # Reset column info since it may be stale.
- reset_column_information
- if columns_hash['sessid']
- def self.find_by_session_id(*args)
- find_by_sessid(*args)
- end
-
- define_method(:session_id) { sessid }
- define_method(:session_id=) { |session_id| self.sessid = session_id }
- else
- class << self; remove_possible_method :find_by_session_id; end
-
- def self.find_by_session_id(session_id)
- where(session_id: session_id).first
- end
- end
- end
- end
-
- def initialize(attributes = nil, options = {})
- @data = nil
- super
- end
-
- # Lazy-unmarshal session state.
- def data
- @data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {}
- end
-
- attr_writer :data
-
- # Has the session been loaded yet?
- def loaded?
- @data
- end
-
- private
- def marshal_data!
- return false unless loaded?
- write_attribute(@@data_column_name, self.class.marshal(data))
- end
-
- # Ensures that the data about to be stored in the database is not
- # larger than the data storage column. Raises
- # ActionController::SessionOverflowError.
- def raise_on_session_data_overflow!
- return false unless loaded?
- limit = self.class.data_column_size_limit
- if limit and read_attribute(@@data_column_name).size > limit
- raise ActionController::SessionOverflowError
- end
- end
- end
-
- # A barebones session store which duck-types with the default session
- # store but bypasses Active Record and issues SQL directly. This is
- # an example session model class meant as a basis for your own classes.
- #
- # The database connection, table name, and session id and data columns
- # are configurable class attributes. Marshaling and unmarshaling
- # are implemented as class methods that you may override. By default,
- # marshaling data is
- #
- # ::Base64.encode64(Marshal.dump(data))
- #
- # and unmarshaling data is
- #
- # Marshal.load(::Base64.decode64(data))
- #
- # This marshaling behavior is intended to store the widest range of
- # binary session data in a +text+ column. For higher performance,
- # store in a +blob+ column instead and forgo the Base64 encoding.
- class SqlBypass
- extend ClassMethods
-
- ##
- # :singleton-method:
- # The table name defaults to 'sessions'.
- cattr_accessor :table_name
- @@table_name = 'sessions'
-
- ##
- # :singleton-method:
- # The session id field defaults to 'session_id'.
- cattr_accessor :session_id_column
- @@session_id_column = 'session_id'
-
- ##
- # :singleton-method:
- # The data field defaults to 'data'.
- cattr_accessor :data_column
- @@data_column = 'data'
-
- class << self
- alias :data_column_name :data_column
-
- # Use the ActiveRecord::Base.connection by default.
- attr_writer :connection
-
- # Use the ActiveRecord::Base.connection_pool by default.
- attr_writer :connection_pool
-
- def connection
- @connection ||= ActiveRecord::Base.connection
- end
-
- def connection_pool
- @connection_pool ||= ActiveRecord::Base.connection_pool
- end
-
- # Look up a session by id and unmarshal its data if found.
- def find_by_session_id(session_id)
- if record = connection.select_one("SELECT #{connection.quote_column_name(data_column)} AS data FROM #{@@table_name} WHERE #{connection.quote_column_name(@@session_id_column)}=#{connection.quote(session_id.to_s)}")
- new(:session_id => session_id, :marshaled_data => record['data'])
- end
- end
- end
-
- delegate :connection, :connection=, :connection_pool, :connection_pool=, :to => self
-
- attr_reader :session_id, :new_record
- alias :new_record? :new_record
-
- attr_writer :data
-
- # Look for normal and marshaled data, self.find_by_session_id's way of
- # telling us to postpone unmarshaling until the data is requested.
- # We need to handle a normal data attribute in case of a new record.
- def initialize(attributes)
- @session_id = attributes[:session_id]
- @data = attributes[:data]
- @marshaled_data = attributes[:marshaled_data]
- @new_record = @marshaled_data.nil?
- end
-
- # Returns true if the record is persisted, i.e. it's not a new record
- def persisted?
- !@new_record
- end
-
- # Lazy-unmarshal session state.
- def data
- unless @data
- if @marshaled_data
- @data, @marshaled_data = self.class.unmarshal(@marshaled_data) || {}, nil
- else
- @data = {}
- end
- end
- @data
- end
-
- def loaded?
- @data
- end
-
- def save
- return false unless loaded?
- marshaled_data = self.class.marshal(data)
- connect = connection
-
- if @new_record
- @new_record = false
- connect.update <<-end_sql, 'Create session'
- INSERT INTO #{table_name} (
- #{connect.quote_column_name(session_id_column)},
- #{connect.quote_column_name(data_column)} )
- VALUES (
- #{connect.quote(session_id)},
- #{connect.quote(marshaled_data)} )
- end_sql
- else
- connect.update <<-end_sql, 'Update session'
- UPDATE #{table_name}
- SET #{connect.quote_column_name(data_column)}=#{connect.quote(marshaled_data)}
- WHERE #{connect.quote_column_name(session_id_column)}=#{connect.quote(session_id)}
- end_sql
- end
- end
-
- def destroy
- return if @new_record
-
- connect = connection
- connect.delete <<-end_sql, 'Destroy session'
- DELETE FROM #{table_name}
- WHERE #{connect.quote_column_name(session_id_column)}=#{connect.quote(session_id.to_s)}
- end_sql
- end
- end
-
- # The class used for session storage. Defaults to
- # ActiveRecord::SessionStore::Session
- cattr_accessor :session_class
- self.session_class = Session
-
- SESSION_RECORD_KEY = 'rack.session.record'
- ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY
-
- private
- def get_session(env, sid)
- Base.silence do
- unless sid and session = @@session_class.find_by_session_id(sid)
- # If the sid was nil or if there is no pre-existing session under the sid,
- # force the generation of a new sid and associate a new session associated with the new sid
- sid = generate_sid
- session = @@session_class.new(:session_id => sid, :data => {})
- end
- env[SESSION_RECORD_KEY] = session
- [sid, session.data]
- end
- end
-
- def set_session(env, sid, session_data, options)
- Base.silence do
- record = get_session_model(env, sid)
- record.data = session_data
- return false unless record.save
-
- session_data = record.data
- if session_data && session_data.respond_to?(:each_value)
- session_data.each_value do |obj|
- obj.clear_association_cache if obj.respond_to?(:clear_association_cache)
- end
- end
- end
-
- sid
- end
-
- def destroy_session(env, session_id, options)
- if sid = current_session_id(env)
- Base.silence do
- get_session_model(env, sid).destroy
- env[SESSION_RECORD_KEY] = nil
- end
- end
-
- generate_sid unless options[:drop]
- end
-
- def get_session_model(env, sid)
- if env[ENV_SESSION_OPTIONS_KEY][:id].nil?
- env[SESSION_RECORD_KEY] = find_session(sid)
- else
- env[SESSION_RECORD_KEY] ||= find_session(sid)
- end
- end
-
- def find_session(id)
- @@session_class.find_by_session_id(id) ||
- @@session_class.new(:session_id => id, :data => {})
- end
- end
-end
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index 5151f349b7..b4013ecc1e 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -41,7 +41,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_attribute :stored_attributes, instance_writer: false
+ class_attribute :stored_attributes, instance_accessor: false
self.stored_attributes = {}
end
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index fb3dfc2730..b41cc68b6a 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -115,7 +115,7 @@ module ActiveRecord
end
def local_database?(configuration)
- configuration['host'].in?(LOCAL_HOSTS) || configuration['host'].blank?
+ configuration['host'].blank? || LOCAL_HOSTS.include?(configuration['host'])
end
end
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 9cb9b4627b..8acbc08e09 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -293,12 +293,10 @@ module ActiveRecord
begin
status = yield
rescue ActiveRecord::Rollback
- if defined?(@_start_transaction_state)
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
- end
+ @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
status = nil
end
-
+
raise ActiveRecord::Rollback unless status
end
status
@@ -308,7 +306,6 @@ module ActiveRecord
# Save the new record state and id of a record so it can be restored later if a transaction fails.
def remember_transaction_record_state #:nodoc:
- @_start_transaction_state ||= {}
@_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
@_start_transaction_state[:new_record] = @new_record
@_start_transaction_state[:destroyed] = @destroyed
@@ -317,18 +314,16 @@ module ActiveRecord
# Clear the new record state and id of a record.
def clear_transaction_record_state #:nodoc:
- if defined?(@_start_transaction_state)
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
- remove_instance_variable(:@_start_transaction_state) if @_start_transaction_state[:level] < 1
- end
+ @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
+ @_start_transaction_state.clear if @_start_transaction_state[:level] < 1
end
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
def restore_transaction_record_state(force = false) #:nodoc:
- if defined?(@_start_transaction_state)
+ unless @_start_transaction_state.empty?
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
- if @_start_transaction_state[:level] < 1
- restore_state = remove_instance_variable(:@_start_transaction_state)
+ if @_start_transaction_state[:level] < 1 || force
+ restore_state = @_start_transaction_state
was_frozen = @attributes.frozen?
@attributes = @attributes.dup if was_frozen
@new_record = restore_state[:new_record]
@@ -340,13 +335,14 @@ module ActiveRecord
@attributes_cache.delete(self.class.primary_key)
end
@attributes.freeze if was_frozen
+ @_start_transaction_state.clear
end
end
end
# Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
def transaction_record_state(state) #:nodoc:
- @_start_transaction_state[state] if defined?(@_start_transaction_state)
+ @_start_transaction_state[state]
end
# Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
diff --git a/activerecord/lib/rails/generators/active_record/session_migration/session_migration_generator.rb b/activerecord/lib/rails/generators/active_record/session_migration/session_migration_generator.rb
deleted file mode 100644
index a976571dee..0000000000
--- a/activerecord/lib/rails/generators/active_record/session_migration/session_migration_generator.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require 'rails/generators/active_record'
-
-module ActiveRecord
- module Generators
- class SessionMigrationGenerator < Base
- argument :name, :type => :string, :default => "add_sessions_table"
-
- def create_migration_file
- migration_template "migration.rb", "db/migrate/#{file_name}.rb"
- end
-
- protected
-
- def session_table_name
- current_table_name = ActiveRecord::SessionStore::Session.table_name
- if current_table_name.in?(["sessions", "session"])
- current_table_name = (ActiveRecord::Base.pluralize_table_names ? 'session'.pluralize : 'session')
- end
- current_table_name
- end
-
- end
- end
-end
diff --git a/activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb
deleted file mode 100644
index 9ea3248513..0000000000
--- a/activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class <%= migration_class_name %> < ActiveRecord::Migration
- def change
- create_table :<%= session_table_name %> do |t|
- t.string :session_id, :null => false
- t.text :data
- t.timestamps
- end
-
- add_index :<%= session_table_name %>, :session_id
- add_index :<%= session_table_name %>, :updated_at
- end
-end
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index c3f82bc63d..4bccd2cc59 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -128,11 +128,12 @@ class MysqlConnectionTest < ActiveRecord::TestCase
assert_equal [["STRICT_ALL_TABLES"]], result.rows
end
- def test_mysql_strict_mode_disabled
+ def test_mysql_strict_mode_disabled_dont_override_global_sql_mode
run_without_connection do |orig_connection|
ActiveRecord::Model.establish_connection(orig_connection.merge({:strict => false}))
- result = ActiveRecord::Model.connection.exec_query "SELECT @@SESSION.sql_mode"
- assert_equal [['']], result.rows
+ global_sql_mode = ActiveRecord::Model.connection.exec_query "SELECT @@GLOBAL.sql_mode"
+ session_sql_mode = ActiveRecord::Model.connection.exec_query "SELECT @@SESSION.sql_mode"
+ assert_equal global_sql_mode.rows, session_sql_mode.rows
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 276c499276..c63e4fe5b6 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -44,11 +44,12 @@ class MysqlConnectionTest < ActiveRecord::TestCase
assert_equal [["STRICT_ALL_TABLES"]], result.rows
end
- def test_mysql_strict_mode_disabled
+ def test_mysql_strict_mode_disabled_dont_override_global_sql_mode
run_without_connection do |orig_connection|
ActiveRecord::Model.establish_connection(orig_connection.merge({:strict => false}))
- result = ActiveRecord::Model.connection.exec_query "SELECT @@SESSION.sql_mode"
- assert_equal [['']], result.rows
+ global_sql_mode = ActiveRecord::Model.connection.exec_query "SELECT @@GLOBAL.sql_mode"
+ session_sql_mode = ActiveRecord::Model.connection.exec_query "SELECT @@SESSION.sql_mode"
+ assert_equal global_sql_mode.rows, session_sql_mode.rows
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 202f7e27c5..1ff307c735 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -85,16 +85,17 @@ module ActiveRecord
assert @connection.active?
original_connection_pid = @connection.query('select pg_backend_pid()')
- # Fail with bad connection after next query attempt.
- connection_class = class << @connection ; self ; end
- connection_class.class_eval <<-CODE
+ # Fail with bad connection on next query attempt.
+ raw_connection = @connection.raw_connection
+ raw_connection_class = class << raw_connection ; self ; end
+ raw_connection_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
def query_fake(*args)
- if @called ||= false
- @connection.stubs(:status).returns(PCconn::CONNECTION_BAD)
+ if !( @called ||= false )
+ self.stubs(:status).returns(PGconn::CONNECTION_BAD)
+ @called = true
raise PGError
else
- @called = true
- @connection.unstub(:status)
+ self.unstub(:status)
query_unfake(*args)
end
end
@@ -107,13 +108,13 @@ module ActiveRecord
@connection.verify!
new_connection_pid = @connection.query('select pg_backend_pid()')
ensure
- connection_class.class_eval <<-CODE
+ raw_connection_class.class_eval <<-CODE
alias query query_unfake
undef query_fake
CODE
end
- assert_equal original_connection_pid, new_connection_pid, "Should have a new underlying connection pid"
+ assert_not_equal original_connection_pid, new_connection_pid, "Should have a new underlying connection pid"
end
# Must have with_manual_interventions set to true for this
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index ec7e4f5fb7..5f7825783b 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -524,13 +524,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_invalid_belongs_to_dependent_option_nullify_raises_exception
assert_raise ArgumentError do
- Author.belongs_to :special_author_address, :dependent => :nullify
+ Class.new(Author).belongs_to :special_author_address, :dependent => :nullify
end
end
def test_invalid_belongs_to_dependent_option_restrict_raises_exception
assert_raise ArgumentError do
- Author.belongs_to :special_author_address, :dependent => :restrict
+ Class.new(Author).belongs_to :special_author_address, :dependent => :restrict
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 43440c1146..04714f42e9 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1091,9 +1091,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_restrict
- option_before = ActiveRecord::Base.dependent_restrict_raises
- ActiveRecord::Base.dependent_restrict_raises = true
-
firm = RestrictedFirm.create!(:name => 'restrict')
firm.companies.create(:name => 'child')
@@ -1101,15 +1098,25 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
assert RestrictedFirm.exists?(:name => 'restrict')
assert firm.companies.exists?(:name => 'child')
- ensure
- ActiveRecord::Base.dependent_restrict_raises = option_before
end
- def test_restrict_when_dependent_restrict_raises_config_set_to_false
- option_before = ActiveRecord::Base.dependent_restrict_raises
- ActiveRecord::Base.dependent_restrict_raises = false
+ def test_restrict_is_deprecated
+ klass = Class.new(ActiveRecord::Base)
+ assert_deprecated { klass.has_many :posts, dependent: :restrict }
+ end
- firm = RestrictedFirm.create!(:name => 'restrict')
+ def test_restrict_with_exception
+ firm = RestrictedWithExceptionFirm.create!(:name => 'restrict')
+ firm.companies.create(:name => 'child')
+
+ assert !firm.companies.empty?
+ assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
+ assert RestrictedWithExceptionFirm.exists?(:name => 'restrict')
+ assert firm.companies.exists?(:name => 'child')
+ end
+
+ def test_restrict_with_error
+ firm = RestrictedWithErrorFirm.create!(:name => 'restrict')
firm.companies.create(:name => 'child')
assert !firm.companies.empty?
@@ -1119,10 +1126,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert !firm.errors.empty?
assert_equal "Cannot delete record because dependent companies exist", firm.errors[:base].first
- assert RestrictedFirm.exists?(:name => 'restrict')
+ assert RestrictedWithErrorFirm.exists?(:name => 'restrict')
assert firm.companies.exists?(:name => 'child')
- ensure
- ActiveRecord::Base.dependent_restrict_raises = option_before
end
def test_included_in_collection
@@ -1602,18 +1607,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [bulb1, bulb3], result
end
- def test_building_has_many_association_with_restrict_dependency
- option_before = ActiveRecord::Base.dependent_restrict_raises
- ActiveRecord::Base.dependent_restrict_raises = true
-
- klass = Class.new(ActiveRecord::Base)
-
- assert_deprecated { klass.has_many :companies, :dependent => :restrict }
- assert_not_deprecated { klass.has_many :companies }
- ensure
- ActiveRecord::Base.dependent_restrict_raises = option_before
- end
-
def test_collection_association_with_private_kernel_method
firm = companies(:first_firm)
assert_equal [accounts(:signals37)], firm.accounts.open
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 112735839f..8bc633f2b5 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -156,10 +156,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_nothing_raised { firm.destroy }
end
- def test_dependence_with_restrict
- option_before = ActiveRecord::Base.dependent_restrict_raises
- ActiveRecord::Base.dependent_restrict_raises = true
-
+ def test_restrict
firm = RestrictedFirm.create!(:name => 'restrict')
firm.create_account(:credit_limit => 10)
@@ -168,38 +165,26 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
assert RestrictedFirm.exists?(:name => 'restrict')
assert firm.account.present?
- ensure
- ActiveRecord::Base.dependent_restrict_raises = option_before
end
- def test_dependence_with_restrict_with_dependent_restrict_raises_config_set_to_false
- option_before = ActiveRecord::Base.dependent_restrict_raises
- ActiveRecord::Base.dependent_restrict_raises = false
+ def test_restrict_is_deprecated
+ klass = Class.new(ActiveRecord::Base)
+ assert_deprecated { klass.has_one :post, dependent: :restrict }
+ end
- firm = RestrictedFirm.create!(:name => 'restrict')
+ def test_restrict_with_exception
+ firm = RestrictedWithExceptionFirm.create!(:name => 'restrict')
firm.create_account(:credit_limit => 10)
assert_not_nil firm.account
- firm.destroy
-
- assert !firm.errors.empty?
- assert_equal "Cannot delete record because a dependent account exists", firm.errors[:base].first
- assert RestrictedFirm.exists?(:name => 'restrict')
+ assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
+ assert RestrictedWithExceptionFirm.exists?(:name => 'restrict')
assert firm.account.present?
- ensure
- ActiveRecord::Base.dependent_restrict_raises = option_before
end
- def test_dependence_with_restrict_with_dependent_restrict_raises_config_set_to_false_and_attribute_name
- old_backend = I18n.backend
- I18n.backend = I18n::Backend::Simple.new
- I18n.backend.store_translations 'en', :activerecord => {:attributes => {:restricted_firm => {:account => "account model"}}}
-
- option_before = ActiveRecord::Base.dependent_restrict_raises
- ActiveRecord::Base.dependent_restrict_raises = false
-
- firm = RestrictedFirm.create!(:name => 'restrict')
+ def test_restrict_with_error
+ firm = RestrictedWithErrorFirm.create!(:name => 'restrict')
firm.create_account(:credit_limit => 10)
assert_not_nil firm.account
@@ -207,12 +192,9 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
firm.destroy
assert !firm.errors.empty?
- assert_equal "Cannot delete record because a dependent account model exists", firm.errors[:base].first
- assert RestrictedFirm.exists?(:name => 'restrict')
+ assert_equal "Cannot delete record because a dependent account exists", firm.errors[:base].first
+ assert RestrictedWithErrorFirm.exists?(:name => 'restrict')
assert firm.account.present?
- ensure
- ActiveRecord::Base.dependent_restrict_raises = option_before
- I18n.backend = old_backend
end
def test_successful_build_association
@@ -524,15 +506,16 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_equal car.id, bulb.attributes_after_initialize['car_id']
end
- def test_building_has_one_association_with_dependent_restrict
- option_before = ActiveRecord::Base.dependent_restrict_raises
- ActiveRecord::Base.dependent_restrict_raises = true
+ def test_has_one_transaction
+ company = companies(:first_firm)
+ account = Account.find(1)
- klass = Class.new(ActiveRecord::Base)
+ company.account # force loading
+ assert_no_queries { company.account = account }
- assert_deprecated { klass.has_one :account, :dependent => :restrict }
- assert_not_deprecated { klass.has_one :account }
- ensure
- ActiveRecord::Base.dependent_restrict_raises = option_before
+ company.account = nil
+ assert_no_queries { company.account = nil }
+ account = Account.find(2)
+ assert_queries { company.account = account }
end
end
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index 8cb8a5a861..aad48e7ce9 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -259,6 +259,12 @@ class InverseHasManyTests < ActiveRecord::TestCase
assert_equal m.name, i.man.name, "Name of man should be the same after changes to replaced-child-owned instance"
end
+ def test_parent_instance_should_be_shared_with_first_and_last_child
+ man = Man.first
+ assert man.interests.first.man.equal? man
+ assert man.interests.last.man.equal? man
+ end
+
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.first.secret_interests }
end
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index d4b7960047..86893ec4b3 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -385,7 +385,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_polymorphic_has_one
- assert_equal Tagging.find(1,2).sort_by { |t| t.id }, authors(:david).tagging
+ assert_equal Tagging.find(1,2).sort_by { |t| t.id }, authors(:david).taggings_2
end
def test_has_many_through_polymorphic_has_many
@@ -453,7 +453,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert saved_post.tags.include?(new_tag)
assert new_tag.persisted?
- assert new_tag.in?(saved_post.reload.tags(true))
+ assert saved_post.reload.tags(true).include?(new_tag)
new_post = Post.new(:title => "Association replacmenet works!", :body => "You best believe it.")
@@ -466,7 +466,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
new_post.save!
assert new_post.persisted?
- assert saved_tag.in?(new_post.reload.tags(true))
+ assert new_post.reload.tags(true).include?(saved_tag)
assert !posts(:thinking).tags.build.persisted?
assert !posts(:thinking).tags.new.persisted?
diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb
index 55d45d9d93..da5d9d8c2a 100644
--- a/activerecord/test/cases/attribute_methods/read_test.rb
+++ b/activerecord/test/cases/attribute_methods/read_test.rb
@@ -47,13 +47,13 @@ module ActiveRecord
instance = @klass.new
@klass.column_names.each do |name|
- assert !name.in?(instance.methods.map(&:to_s))
+ assert !instance.methods.map(&:to_s).include?(name)
end
@klass.define_attribute_methods
@klass.column_names.each do |name|
- assert name.in?(instance.methods.map(&:to_s)), "#{name} is not defined"
+ assert instance.methods.map(&:to_s).include?(name), "#{name} is not defined"
end
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 807971d678..4bc68acd13 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -34,7 +34,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert t.attribute_present?("written_on")
assert !t.attribute_present?("content")
assert !t.attribute_present?("author_name")
-
end
def test_attribute_present_with_booleans
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 062f196a12..63981a68a9 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -231,6 +231,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal 11, Topic.find(1).written_on.sec
assert_equal 223300, Topic.find(1).written_on.usec
assert_equal 9900, Topic.find(2).written_on.usec
+ assert_equal 129346, Topic.find(3).written_on.usec
end
end
@@ -603,6 +604,12 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal "changed", post.body
end
+ def test_attr_readonly_is_class_level_setting
+ post = ReadonlyTitlePost.new
+ assert_raise(NoMethodError) { post._attr_readonly = [:title] }
+ assert_deprecated { post._attr_readonly }
+ end
+
def test_non_valid_identifier_column_name
weird = Weird.create('a$b' => 'value')
weird.reload
diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb
index cd3d19e783..ee443741ca 100644
--- a/activerecord/test/cases/counter_cache_test.rb
+++ b/activerecord/test/cases/counter_cache_test.rb
@@ -8,9 +8,11 @@ require 'models/category'
require 'models/categorization'
require 'models/dog'
require 'models/dog_lover'
+require 'models/person'
+require 'models/friendship'
class CounterCacheTest < ActiveRecord::TestCase
- fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers
+ fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers, :people, :friendships
class ::SpecialTopic < ::Topic
has_many :special_replies, :foreign_key => 'parent_id'
@@ -109,4 +111,11 @@ class CounterCacheTest < ActiveRecord::TestCase
Topic.update_counters([t1.id, t2.id], :replies_count => 2)
end
end
+
+ test "reset the right counter if two have the same foreign key" do
+ michael = people(:michael)
+ assert_nothing_raised(ActiveRecord::StatementInvalid) do
+ Person.reset_counters(michael.id, :followers)
+ end
+ end
end
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index 81c5850a47..deaf5252db 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -94,7 +94,7 @@ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
assert_equal 0, klass.columns_hash['zero'].default
assert !klass.columns_hash['zero'].null
# 0 in MySQL 4, nil in 5.
- assert klass.columns_hash['omit'].default.in?([0, nil])
+ assert [0, nil].include?(klass.columns_hash['omit'].default)
assert !klass.columns_hash['omit'].null
assert_raise(ActiveRecord::StatementInvalid) { klass.create! }
diff --git a/activerecord/test/cases/deprecated_dynamic_methods_test.rb b/activerecord/test/cases/deprecated_dynamic_methods_test.rb
index fe307bc49b..392f5f4cd5 100644
--- a/activerecord/test/cases/deprecated_dynamic_methods_test.rb
+++ b/activerecord/test/cases/deprecated_dynamic_methods_test.rb
@@ -1,4 +1,4 @@
-# This file should be deleted when active_record_deprecated_finders is removed as
+# This file should be deleted when activerecord-deprecated_finders is removed as
# a dependency.
#
# It is kept for now as there is some fairly nuanced behaviour in the dynamic
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 248f4efe3e..92677b9926 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -525,6 +525,21 @@ 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
+ in_time_zone 'Paris' do
+ target = Class.new(ActiveRecord::Base)
+ target.table_name = 'pirates'
+
+ created_on = Time.now
+
+ pirate = target.create(:created_on => created_on)
+ pirate.reload # Here mysql truncate the usec value to 0
+
+ pirate.created_on = created_on
+ assert !pirate.created_on_changed?
+ end
+ end
+
private
def with_partial_updates(klass, on = true)
old = klass.partial_updates?
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 018064233a..4c6d4666ed 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -19,9 +19,6 @@ require 'support/connection'
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
-# Avoid deprecation warning setting dependent_restrict_raises to false. The default is true
-ActiveRecord::Base.dependent_restrict_raises = false
-
# Connect to the database
ARTest.connect
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index afb0bd6fd9..2392516395 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -3,6 +3,7 @@ require "cases/helper"
require 'models/person'
require 'models/job'
require 'models/reader'
+require 'models/ship'
require 'models/legacy_thing'
require 'models/reference'
require 'models/string_key_object'
@@ -18,8 +19,8 @@ class LockWithCustomColumnWithoutDefault < ActiveRecord::Base
self.locking_column = :custom_lock_version
end
-class ReadonlyFirstNamePerson < Person
- attr_readonly :first_name
+class ReadonlyNameShip < Ship
+ attr_readonly :name
end
class OptimisticLockingTest < ActiveRecord::TestCase
@@ -200,15 +201,15 @@ class OptimisticLockingTest < ActiveRecord::TestCase
end
def test_readonly_attributes
- assert_equal Set.new([ 'first_name' ]), ReadonlyFirstNamePerson.readonly_attributes
+ assert_equal Set.new([ 'name' ]), ReadonlyNameShip.readonly_attributes
- p = ReadonlyFirstNamePerson.create(:first_name => "unchangeable name")
- p.reload
- assert_equal "unchangeable name", p.first_name
+ s = ReadonlyNameShip.create(:name => "unchangeable name")
+ s.reload
+ assert_equal "unchangeable name", s.name
- p.update_attributes(:first_name => "changed name")
- p.reload
- assert_equal "unchangeable name", p.first_name
+ s.update_attributes(:name => "changed name")
+ s.reload
+ assert_equal "unchangeable name", s.name
end
def test_quote_table_name
diff --git a/activerecord/test/cases/mass_assignment_security_test.rb b/activerecord/test/cases/mass_assignment_security_test.rb
index 73a01906b9..a36b2c2506 100644
--- a/activerecord/test/cases/mass_assignment_security_test.rb
+++ b/activerecord/test/cases/mass_assignment_security_test.rb
@@ -313,7 +313,7 @@ class MassAssignmentSecurityTest < ActiveRecord::TestCase
end
-# This class should be deleted when we removed active_record_deprecated_finders as a
+# This class should be deleted when we remove activerecord-deprecated_finders as a
# dependency.
class MassAssignmentSecurityDeprecatedFindersTest < ActiveRecord::TestCase
include MassAssignmentTestHelpers
diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb
index 9584d5dd06..b88db384a0 100644
--- a/activerecord/test/cases/migration/column_attributes_test.rb
+++ b/activerecord/test/cases/migration/column_attributes_test.rb
@@ -7,6 +7,14 @@ module ActiveRecord
self.use_transactional_fixtures = false
+ def test_add_column_newline_default
+ string = "foo\nbar"
+ add_column 'test_models', 'command', :string, :default => string
+ TestModel.reset_column_information
+
+ assert_equal string, TestModel.new.command
+ end
+
def test_add_remove_single_field_using_string_arguments
refute TestModel.column_methods_hash.key?(:last_name)
diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb
index 06d6596725..42461e8ecb 100644
--- a/activerecord/test/cases/multiple_db_test.rb
+++ b/activerecord/test/cases/multiple_db_test.rb
@@ -1,9 +1,7 @@
require "cases/helper"
require 'models/entrant'
require 'models/bird'
-
-# So we can test whether Course.connection survives a reload.
-require_dependency 'models/course'
+require 'models/course'
class MultipleDbTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 3a234f0cc1..725cff8f01 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -846,13 +846,7 @@ class TestNestedAttributesOnAHasAndBelongsToManyAssociation < ActiveRecord::Test
include NestedAttributesOnACollectionAssociationTests
end
-class TestNestedAttributesLimit < ActiveRecord::TestCase
- def setup
- Pirate.accepts_nested_attributes_for :parrots, :limit => 2
-
- @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
- end
-
+module NestedAttributesLimitTests
def teardown
Pirate.accepts_nested_attributes_for :parrots, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
end
@@ -876,6 +870,36 @@ class TestNestedAttributesLimit < ActiveRecord::TestCase
end
end
+class TestNestedAttributesLimitNumeric < ActiveRecord::TestCase
+ def setup
+ Pirate.accepts_nested_attributes_for :parrots, :limit => 2
+
+ @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ end
+
+ include NestedAttributesLimitTests
+end
+
+class TestNestedAttributesLimitSymbol < ActiveRecord::TestCase
+ def setup
+ Pirate.accepts_nested_attributes_for :parrots, :limit => :parrots_limit
+
+ @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?", :parrots_limit => 2)
+ end
+
+ include NestedAttributesLimitTests
+end
+
+class TestNestedAttributesLimitProc < ActiveRecord::TestCase
+ def setup
+ Pirate.accepts_nested_attributes_for :parrots, :limit => proc { 2 }
+
+ @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ end
+
+ include NestedAttributesLimitTests
+end
+
class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase
fixtures :owners, :pets
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 5fb54b1ca1..6399111be6 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -19,6 +19,11 @@ module ActiveRecord
assert !relation.loaded, 'relation is not loaded'
end
+ def test_responds_to_model_and_returns_klass
+ relation = Relation.new :a, :b
+ assert_equal :a, relation.model
+ end
+
def test_initialize_single_values
relation = Relation.new :a, :b
(Relation::SINGLE_VALUE_METHODS - [:create_with]).each do |method|
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 0bd48913e1..b91423351e 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -656,6 +656,14 @@ class RelationTest < ActiveRecord::TestCase
assert_raises(ActiveRecord::ActiveRecordError) { Author.limit(10).delete_all }
end
+ def test_select_takes_a_variable_list_of_args
+ david = developers(:david)
+
+ developer = Developer.where(id: david.id).select(:name, :salary).first
+ assert_equal david.name, developer.name
+ assert_equal david.salary, developer.salary
+ end
+
def test_select_argument_error
assert_raises(ArgumentError) { Developer.select }
end
@@ -668,6 +676,25 @@ class RelationTest < ActiveRecord::TestCase
assert_equal [developers(:poor_jamis)], dev_with_count.to_a
end
+ def test_relation_merging_with_arel_equalities_keeps_last_equality
+ devs = Developer.where(Developer.arel_table[:salary].eq(80000)).merge(
+ Developer.where(Developer.arel_table[:salary].eq(9000))
+ )
+ assert_equal [developers(:poor_jamis)], devs.to_a
+ end
+
+ def test_relation_merging_with_arel_equalities_keeps_last_equality_with_non_attribute_left_hand
+ salary_attr = Developer.arel_table[:salary]
+ devs = Developer.where(
+ Arel::Nodes::NamedFunction.new('abs', [salary_attr]).eq(80000)
+ ).merge(
+ Developer.where(
+ Arel::Nodes::NamedFunction.new('abs', [salary_attr]).eq(9000)
+ )
+ )
+ assert_equal [developers(:poor_jamis)], devs.to_a
+ end
+
def test_relation_merging_with_eager_load
relations = []
relations << Post.order('comments.id DESC').merge(Post.eager_load(:last_comment)).merge(Post.all)
@@ -1122,6 +1149,10 @@ class RelationTest < ActiveRecord::TestCase
assert_equal authors(:david), Author.order('id DESC , name DESC').last
end
+ def test_update_all_with_blank_argument
+ assert_raises(ArgumentError) { Comment.update_all({}) }
+ end
+
def test_update_all_with_joins
comments = Comment.joins(:post).where('posts.id' => posts(:welcome).id)
count = comments.count
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index ce167509c1..10d8ccc711 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -53,9 +53,8 @@ class SerializationTest < ActiveRecord::TestCase
end
def test_serialized_attributes_are_class_level_settings
- assert_raise NoMethodError do
- topic = Topic.new
- topic.serialized_attributes = []
- end
+ topic = Topic.new
+ assert_raise(NoMethodError) { topic.serialized_attributes = [] }
+ assert_deprecated { topic.serialized_attributes }
end
end
diff --git a/activerecord/test/cases/session_store/session_test.rb b/activerecord/test/cases/session_store/session_test.rb
deleted file mode 100644
index a3b8ab74d9..0000000000
--- a/activerecord/test/cases/session_store/session_test.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-require 'cases/helper'
-require 'action_dispatch'
-require 'active_record/session_store'
-
-module ActiveRecord
- class SessionStore
- class SessionTest < ActiveRecord::TestCase
- self.use_transactional_fixtures = false
-
- attr_reader :session_klass
-
- def setup
- super
- ActiveRecord::Base.connection.schema_cache.clear!
- Session.drop_table! if Session.table_exists?
- @session_klass = Class.new(Session)
- end
-
- def test_data_column_name
- # default column name is 'data'
- assert_equal 'data', Session.data_column_name
- end
-
- def test_table_name
- assert_equal 'sessions', Session.table_name
- end
-
- def test_accessible_attributes
- assert Session.accessible_attributes.include?(:session_id)
- assert Session.accessible_attributes.include?(:data)
- assert Session.accessible_attributes.include?(:marshaled_data)
- end
-
- def test_create_table!
- assert !Session.table_exists?
- Session.create_table!
- assert Session.table_exists?
- Session.drop_table!
- assert !Session.table_exists?
- end
-
- def test_find_by_sess_id_compat
- Session.reset_column_information
- klass = Class.new(Session) do
- def self.session_id_column
- 'sessid'
- end
- end
- klass.create_table!
-
- assert klass.columns_hash['sessid'], 'sessid column exists'
- session = klass.new(:data => 'hello')
- session.sessid = "100"
- session.save!
-
- found = klass.find_by_session_id("100")
- assert_equal session, found
- assert_equal session.sessid, found.session_id
- ensure
- klass.drop_table!
- Session.reset_column_information
- end
-
- def test_find_by_session_id
- Session.create_table!
- session_id = "10"
- s = session_klass.create!(:data => 'world', :session_id => session_id)
- t = session_klass.find_by_session_id(session_id)
- assert_equal s, t
- assert_equal s.data, t.data
- Session.drop_table!
- end
-
- def test_loaded?
- Session.create_table!
- s = Session.new
- assert !s.loaded?, 'session is not loaded'
- end
- end
- end
-end
diff --git a/activerecord/test/cases/session_store/sql_bypass_test.rb b/activerecord/test/cases/session_store/sql_bypass_test.rb
deleted file mode 100644
index b8cf4cf2cc..0000000000
--- a/activerecord/test/cases/session_store/sql_bypass_test.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-require 'cases/helper'
-require 'action_dispatch'
-require 'active_record/session_store'
-
-module ActiveRecord
- class SessionStore
- class SqlBypassTest < ActiveRecord::TestCase
- def setup
- super
- Session.drop_table! if Session.table_exists?
- end
-
- def test_create_table
- assert !Session.table_exists?
- SqlBypass.create_table!
- assert Session.table_exists?
- SqlBypass.drop_table!
- assert !Session.table_exists?
- end
-
- def test_new_record?
- s = SqlBypass.new :data => 'foo', :session_id => 10
- assert s.new_record?, 'this is a new record!'
- end
-
- def test_persisted?
- s = SqlBypass.new :data => 'foo', :session_id => 10
- assert !s.persisted?, 'this is a new record!'
- end
-
- def test_not_loaded?
- s = SqlBypass.new({})
- assert !s.loaded?, 'it is not loaded'
- end
-
- def test_loaded?
- s = SqlBypass.new :data => 'hello'
- assert s.loaded?, 'it is loaded'
- end
-
- def test_save
- SqlBypass.create_table! unless Session.table_exists?
- session_id = 20
- s = SqlBypass.new :data => 'hello', :session_id => session_id
- s.save
- t = SqlBypass.find_by_session_id session_id
- assert_equal s.session_id, t.session_id
- assert_equal s.data, t.data
- end
-
- def test_destroy
- SqlBypass.create_table! unless Session.table_exists?
- session_id = 20
- s = SqlBypass.new :data => 'hello', :session_id => session_id
- s.save
- s.destroy
- assert_nil SqlBypass.find_by_session_id session_id
- end
-
- def test_data_column
- SqlBypass.drop_table! if exists = Session.table_exists?
- old, SqlBypass.data_column = SqlBypass.data_column, 'foo'
- SqlBypass.create_table!
-
- session_id = 20
- SqlBypass.new(:data => 'hello', :session_id => session_id).save
- assert_equal 'hello', SqlBypass.find_by_session_id(session_id).data
- ensure
- SqlBypass.drop_table!
- SqlBypass.data_column = old
- SqlBypass.create_table! if exists
- end
- end
- end
-end
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index 3e60b62fd5..fb0d116c08 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -122,9 +122,8 @@ class StoreTest < ActiveRecord::TestCase
end
test "stores_attributes are class level settings" do
- assert_raise NoMethodError do
- @john.stored_attributes = {}
- end
+ assert_raise(NoMethodError) { @john.stored_attributes = Hash.new }
+ assert_raise(NoMethodError) { @john.stored_attributes }
end
end
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index 7444dc5de1..bb034848e1 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -114,9 +114,12 @@ class TimestampTest < ActiveRecord::TestCase
end
def test_saving_a_record_with_a_belongs_to_that_specifies_touching_a_specific_attribute_the_parent_should_update_that_attribute
- Pet.belongs_to :owner, :touch => :happy_at
+ klass = Class.new(ActiveRecord::Base) do
+ def self.name; 'Pet'; end
+ belongs_to :owner, :touch => :happy_at
+ end
- pet = Pet.first
+ pet = klass.first
owner = pet.owner
previously_owner_happy_at = owner.happy_at
@@ -124,14 +127,15 @@ class TimestampTest < ActiveRecord::TestCase
pet.save
assert_not_equal previously_owner_happy_at, pet.owner.happy_at
- ensure
- Pet.belongs_to :owner, :touch => true
end
def test_touching_a_record_with_a_belongs_to_that_uses_a_counter_cache_should_update_the_parent
- Pet.belongs_to :owner, :counter_cache => :use_count, :touch => true
+ klass = Class.new(ActiveRecord::Base) do
+ def self.name; 'Pet'; end
+ belongs_to :owner, :counter_cache => :use_count, :touch => true
+ end
- pet = Pet.first
+ pet = klass.first
owner = pet.owner
owner.update_columns(happy_at: 3.days.ago)
previously_owner_updated_at = owner.updated_at
@@ -140,15 +144,15 @@ class TimestampTest < ActiveRecord::TestCase
pet.save
assert_not_equal previously_owner_updated_at, pet.owner.updated_at
- ensure
- Pet.belongs_to :owner, :touch => true
end
def test_touching_a_record_touches_parent_record_and_grandparent_record
- Toy.belongs_to :pet, :touch => true
- Pet.belongs_to :owner, :touch => true
+ klass = Class.new(ActiveRecord::Base) do
+ def self.name; 'Toy'; end
+ belongs_to :pet, :touch => true
+ end
- toy = Toy.first
+ toy = klass.first
pet = toy.pet
owner = pet.owner
time = 3.days.ago
@@ -158,8 +162,6 @@ class TimestampTest < ActiveRecord::TestCase
owner.reload
assert_not_equal time, owner.updated_at
- ensure
- Toy.belongs_to :pet
end
def test_timestamp_attributes_for_create
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index a9ccd00fac..0d0de455b3 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -91,18 +91,14 @@ class TransactionTest < ActiveRecord::TestCase
end
def test_raising_exception_in_callback_rollbacks_in_save
- add_exception_raising_after_save_callback_to_topic
-
- begin
- @first.approved = true
- @first.save
- flunk
- rescue => e
- assert_equal "Make the transaction rollback", e.message
- assert !Topic.find(1).approved?
- ensure
- remove_exception_raising_after_save_callback_to_topic
+ def @first.after_save_for_transaction
+ raise 'Make the transaction rollback'
end
+
+ @first.approved = true
+ e = assert_raises(RuntimeError) { @first.save }
+ assert_equal "Make the transaction rollback", e.message
+ assert !Topic.find(1).approved?
end
def test_update_attributes_should_rollback_on_failure
@@ -125,85 +121,85 @@ class TransactionTest < ActiveRecord::TestCase
end
def test_cancellation_from_before_destroy_rollbacks_in_destroy
- add_cancelling_before_destroy_with_db_side_effect_to_topic
- begin
- nbooks_before_destroy = Book.count
- status = @first.destroy
- assert !status
- assert_nothing_raised(ActiveRecord::RecordNotFound) { @first.reload }
- assert_equal nbooks_before_destroy, Book.count
- ensure
- remove_cancelling_before_destroy_with_db_side_effect_to_topic
- end
+ add_cancelling_before_destroy_with_db_side_effect_to_topic @first
+ nbooks_before_destroy = Book.count
+ status = @first.destroy
+ assert !status
+ @first.reload
+ assert_equal nbooks_before_destroy, Book.count
end
- def test_cancellation_from_before_filters_rollbacks_in_save
- %w(validation save).each do |filter|
- send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic")
- begin
- nbooks_before_save = Book.count
- original_author_name = @first.author_name
- @first.author_name += '_this_should_not_end_up_in_the_db'
- status = @first.save
- assert !status
- assert_equal original_author_name, @first.reload.author_name
- assert_equal nbooks_before_save, Book.count
- ensure
- send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic")
- end
+ %w(validation save).each do |filter|
+ define_method("test_cancellation_from_before_filters_rollbacks_in_#{filter}") do
+ send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic", @first)
+ nbooks_before_save = Book.count
+ original_author_name = @first.author_name
+ @first.author_name += '_this_should_not_end_up_in_the_db'
+ status = @first.save
+ assert !status
+ assert_equal original_author_name, @first.reload.author_name
+ assert_equal nbooks_before_save, Book.count
end
- end
- def test_cancellation_from_before_filters_rollbacks_in_save!
- %w(validation save).each do |filter|
- send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic")
+ define_method("test_cancellation_from_before_filters_rollbacks_in_#{filter}!") do
+ send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic", @first)
+ nbooks_before_save = Book.count
+ original_author_name = @first.author_name
+ @first.author_name += '_this_should_not_end_up_in_the_db'
+
begin
- nbooks_before_save = Book.count
- original_author_name = @first.author_name
- @first.author_name += '_this_should_not_end_up_in_the_db'
@first.save!
- flunk
- rescue
- assert_equal original_author_name, @first.reload.author_name
- assert_equal nbooks_before_save, Book.count
- ensure
- send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic")
+ rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved
end
+
+ assert_equal original_author_name, @first.reload.author_name
+ assert_equal nbooks_before_save, Book.count
end
end
def test_callback_rollback_in_create
- new_topic = Topic.new(
- :title => "A new topic",
- :author_name => "Ben",
- :author_email_address => "ben@example.com",
- :written_on => "2003-07-16t15:28:11.2233+01:00",
- :last_read => "2004-04-15",
- :bonus_time => "2005-01-30t15:28:00.00+01:00",
- :content => "Have a nice day",
- :approved => false)
+ topic = Class.new(Topic) {
+ def after_create_for_transaction
+ raise 'Make the transaction rollback'
+ end
+ }
+
+ new_topic = topic.new(:title => "A new topic",
+ :author_name => "Ben",
+ :author_email_address => "ben@example.com",
+ :written_on => "2003-07-16t15:28:11.2233+01:00",
+ :last_read => "2004-04-15",
+ :bonus_time => "2005-01-30t15:28:00.00+01:00",
+ :content => "Have a nice day",
+ :approved => false)
+
new_record_snapshot = !new_topic.persisted?
id_present = new_topic.has_attribute?(Topic.primary_key)
id_snapshot = new_topic.id
# Make sure the second save gets the after_create callback called.
2.times do
- begin
- add_exception_raising_after_create_callback_to_topic
- new_topic.approved = true
- new_topic.save
- flunk
- rescue => e
- assert_equal "Make the transaction rollback", e.message
- assert_equal new_record_snapshot, !new_topic.persisted?, "The topic should have its old persisted value"
- assert_equal id_snapshot, new_topic.id, "The topic should have its old id"
- assert_equal id_present, new_topic.has_attribute?(Topic.primary_key)
- ensure
- remove_exception_raising_after_create_callback_to_topic
- end
+ new_topic.approved = true
+ e = assert_raises(RuntimeError) { new_topic.save }
+ assert_equal "Make the transaction rollback", e.message
+ assert_equal new_record_snapshot, !new_topic.persisted?, "The topic should have its old persisted value"
+ assert_equal id_snapshot, new_topic.id, "The topic should have its old id"
+ assert_equal id_present, new_topic.has_attribute?(Topic.primary_key)
end
end
+ def test_callback_rollback_in_create_with_record_invalid_exception
+ topic = Class.new(Topic) {
+ def after_create_for_transaction
+ raise ActiveRecord::RecordInvalid.new(Author.new)
+ end
+ }
+
+ new_topic = topic.create(:title => "A new topic")
+ assert !new_topic.persisted?, "The topic should not be persisted"
+ assert_nil new_topic.id, "The topic should not have an ID"
+ end
+
def test_nested_explicit_transactions
Topic.transaction do
Topic.transaction do
@@ -461,62 +457,16 @@ class TransactionTest < ActiveRecord::TestCase
end
private
- def define_callback_method(callback_method)
- define_method(callback_method) do
- self.history << [callback_method, :method]
- end
- end
- def add_exception_raising_after_save_callback_to_topic
- Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1
- remove_method(:after_save_for_transaction)
- def after_save_for_transaction
- raise 'Make the transaction rollback'
- end
- eoruby
- end
-
- def remove_exception_raising_after_save_callback_to_topic
- Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1
- remove_method :after_save_for_transaction
- def after_save_for_transaction; end
- eoruby
- end
-
- def add_exception_raising_after_create_callback_to_topic
- Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1
- remove_method(:after_create_for_transaction)
- def after_create_for_transaction
- raise 'Make the transaction rollback'
- end
- eoruby
- end
-
- def remove_exception_raising_after_create_callback_to_topic
- Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1
- remove_method :after_create_for_transaction
- def after_create_for_transaction; end
- eoruby
- end
-
- %w(validation save destroy).each do |filter|
- define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do
- Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1
- remove_method :before_#{filter}_for_transaction
- def before_#{filter}_for_transaction
- Book.create
- false
- end
- eoruby
- end
-
- define_method("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") do
- Topic.class_eval <<-eoruby, __FILE__, __LINE__ + 1
- remove_method :before_#{filter}_for_transaction
- def before_#{filter}_for_transaction; end
- eoruby
+ %w(validation save destroy).each do |filter|
+ define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do |topic|
+ meta = class << topic; self; end
+ meta.send("define_method", "before_#{filter}_for_transaction") do
+ Book.create
+ false
end
end
+ end
end
class TransactionsWithTransactionalFixturesTest < ActiveRecord::TestCase
diff --git a/activerecord/test/fixtures/friendships.yml b/activerecord/test/fixtures/friendships.yml
new file mode 100644
index 0000000000..1ee09175bf
--- /dev/null
+++ b/activerecord/test/fixtures/friendships.yml
@@ -0,0 +1,4 @@
+Connection 1:
+ id: 1
+ person_id: 1
+ friend_id: 2 \ No newline at end of file
diff --git a/activerecord/test/fixtures/people.yml b/activerecord/test/fixtures/people.yml
index 123673a2af..e640a38f1f 100644
--- a/activerecord/test/fixtures/people.yml
+++ b/activerecord/test/fixtures/people.yml
@@ -4,15 +4,18 @@ michael:
primary_contact_id: 2
number1_fan_id: 3
gender: M
+ followers_count: 1
david:
id: 2
first_name: David
primary_contact_id: 3
number1_fan_id: 1
gender: M
+ followers_count: 1
susan:
id: 3
first_name: Susan
primary_contact_id: 2
number1_fan_id: 1
gender: F
+ followers_count: 1
diff --git a/activerecord/test/fixtures/topics.yml b/activerecord/test/fixtures/topics.yml
index 93f48aedc4..2b042bd135 100644
--- a/activerecord/test/fixtures/topics.yml
+++ b/activerecord/test/fixtures/topics.yml
@@ -25,7 +25,7 @@ third:
id: 3
title: The Third Topic of the day
author_name: Carl
- written_on: 2005-07-15t15:28:00.0099+01:00
+ written_on: 2012-08-12t20:24:22.129346+00:00
content: I'm a troll
approved: true
replies_count: 1
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 3157d8fe7f..77f4a2ec87 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -93,8 +93,8 @@ class Author < ActiveRecord::Base
has_many :author_favorites
has_many :favorite_authors, -> { order('name') }, :through => :author_favorites
- has_many :tagging, :through => :posts
has_many :taggings, :through => :posts
+ has_many :taggings_2, :through => :posts, :source => :tagging
has_many :tags, :through => :posts
has_many :post_categories, :through => :posts, :source => :categories
has_many :tagging_tags, :through => :taggings, :source => :tag
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index 5bfbb5e855..75f38d275c 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -115,8 +115,20 @@ class DependentFirm < Company
end
class RestrictedFirm < Company
- has_one :account, -> { order("id") }, :foreign_key => "firm_id", :dependent => :restrict
- has_many :companies, -> { order("id") }, :foreign_key => 'client_of', :dependent => :restrict
+ ActiveSupport::Deprecation.silence do
+ has_one :account, -> { order("id") }, :foreign_key => "firm_id", :dependent => :restrict
+ has_many :companies, -> { order("id") }, :foreign_key => 'client_of', :dependent => :restrict
+ end
+end
+
+class RestrictedWithExceptionFirm < Company
+ has_one :account, -> { order("id") }, :foreign_key => "firm_id", :dependent => :restrict_with_exception
+ has_many :companies, -> { order("id") }, :foreign_key => 'client_of', :dependent => :restrict_with_exception
+end
+
+class RestrictedWithErrorFirm < Company
+ has_one :account, -> { order("id") }, :foreign_key => "firm_id", :dependent => :restrict_with_error
+ has_many :companies, -> { order("id") }, :foreign_key => 'client_of', :dependent => :restrict_with_error
end
class Client < Company
diff --git a/activerecord/test/models/friendship.rb b/activerecord/test/models/friendship.rb
new file mode 100644
index 0000000000..6b4f7acc38
--- /dev/null
+++ b/activerecord/test/models/friendship.rb
@@ -0,0 +1,4 @@
+class Friendship < ActiveRecord::Base
+ belongs_to :friend, class_name: 'Person'
+ belongs_to :follower, foreign_key: 'friend_id', class_name: 'Person', counter_cache: :followers_count
+end
diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb
index 359b29fac3..1134b09d8b 100644
--- a/activerecord/test/models/member.rb
+++ b/activerecord/test/models/member.rb
@@ -24,11 +24,10 @@ class Member < ActiveRecord::Base
has_one :club_category, :through => :club, :source => :category
- has_many :current_memberships
- has_one :club_through_many, :through => :current_memberships, :source => :club
-
has_many :current_memberships, -> { where :favourite => true }
has_many :clubs, :through => :current_memberships
+
+ has_one :club_through_many, :through => :current_memberships, :source => :club
end
class SelfMember < ActiveRecord::Base
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index e204508986..6e6ff29f77 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -8,6 +8,8 @@ class Person < ActiveRecord::Base
has_many :posts_with_no_comments, -> { includes(:comments).where('comments.id is null').references(:comments) },
:through => :readers, :source => :post
+ has_many :followers, foreign_key: 'friend_id', class_name: 'Friendship'
+
has_many :references
has_many :bad_references
has_many :fixed_bad_references, -> { where :favourite => true }, :class_name => 'BadReference'
diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb
index 609b9369a9..170fc2ffe3 100644
--- a/activerecord/test/models/pirate.rb
+++ b/activerecord/test/models/pirate.rb
@@ -53,7 +53,7 @@ class Pirate < ActiveRecord::Base
attributes.delete('_reject_me_if_new').present? && !persisted?
end
- attr_accessor :cancel_save_from_callback
+ attr_accessor :cancel_save_from_callback, :parrots_limit
before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
def cancel_save_callback_method
false
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 6c919a2b02..7c45ca27c0 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -270,6 +270,11 @@ ActiveRecord::Schema.define do
t.string :name
end
+ create_table :friendships, :force => true do |t|
+ t.integer :friend_id
+ t.integer :person_id
+ end
+
create_table :goofy_string_id, :force => true, :id => false do |t|
t.string :id, :null => false
t.string :info
@@ -476,6 +481,7 @@ ActiveRecord::Schema.define do
t.references :number1_fan
t.integer :lock_version, :null => false, :default => 0
t.string :comments
+ t.integer :followers_count, :default => 0
t.references :best_friend
t.references :best_friend_of
t.timestamps
diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb
index c176316a05..92736e0ca9 100644
--- a/activerecord/test/support/connection.rb
+++ b/activerecord/test/support/connection.rb
@@ -1,6 +1,6 @@
require 'active_support/logger'
-require_dependency 'models/college'
-require_dependency 'models/course'
+require 'models/college'
+require 'models/course'
module ARTest
def self.connection_name
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 5fcea299f8..4f57efb35d 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,8 +1,39 @@
## Rails 4.0.0 (unreleased) ##
-* Inflections can now be defined per locale. `singularize` and `pluralize` accept locale as an extra argument. *David Celis*
+* Remove `j` alias for `ERB::Util#json_escape`.
+ The `j` alias is already used for `ActionView::Helpers::JavaScriptHelper#escape_javascript`
+ and both modules are included in the view context that would confuse the developers.
-* `Object#try` will now return nil instead of raise a NoMethodError if the receiving object does not implement the method, but you can still get the old behavior by using the new `Object#try!` *DHH*
+ *Akira Matsuda*
+
+* Replace deprecated `memcache-client` gem with `dalli` in ActiveSupport::Cache::MemCacheStore
+
+ *Guillermo Iguaran*
+
+* Add default values to all `ActiveSupport::NumberHelper` methods, to avoid
+ errors with empty locales or missing values.
+
+ *Carlos Antonio da Silva*
+
+* ActiveSupport::JSON::Variable is deprecated. Define your own #as_json and
+ #encode_json methods for custom JSON string literals.
+
+ *Erich Menge*
+
+* Add String#indent. *fxn & Ace Suares*
+
+* Inflections can now be defined per locale. `singularize` and `pluralize`
+ accept locale as an extra argument.
+
+ *David Celis*
+
+* `Object#try` will now return nil instead of raise a NoMethodError if the
+ receiving object does not implement the method, but you can still get the
+ old behavior by using the new `Object#try!`.
+
+ *DHH*
+
+* `ERB::Util.html_escape` now escapes single quotes. *Santiago Pastorino*
* `Time#change` now works with time values with offsets other than UTC or the local time zone. *Andrew White*
@@ -84,6 +115,33 @@
* Remove deprecated ActiveSupport::JSON::Variable. *Erich Menge*
+## Rails 3.2.8 (Aug 9, 2012) ##
+
+* Fix ActiveSupport integration with Mocha > 0.12.1. *Mike Gunderloy*
+
+* Reverted the deprecation of ActiveSupport::JSON::Variable. *Rafael Mendonça França*
+
+* 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]
+
+* adds a missing require [fixes #6896]
+
+* make sure the inflection rules are loaded when cherry-picking active_support/core_ext/string/inflections.rb [fixes #6884]
+
+* Merge pull request #6857 from rsutphin/as_core_ext_time_missing_require
+
+* 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/lib/active_support.rb b/activesupport/lib/active_support.rb
index 56d6676961..41d77ab6c1 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -37,7 +37,6 @@ module ActiveSupport
autoload :LogSubscriber
autoload :Notifications
- # TODO: Narrow this list down
eager_autoload do
autoload :BacktraceCleaner
autoload :BasicObject
@@ -55,12 +54,12 @@ module ActiveSupport
autoload :OptionMerger
autoload :OrderedHash
autoload :OrderedOptions
- autoload :Rescuable
autoload :StringInquirer
autoload :TaggedLogging
autoload :XmlMini
end
+ autoload :Rescuable
autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
autoload :TestCase
end
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index 5be63af342..2c1ad60d44 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -1,6 +1,5 @@
require 'active_support/core_ext/file/atomic'
require 'active_support/core_ext/string/conversions'
-require 'active_support/core_ext/object/inclusion'
require 'uri/common'
module ActiveSupport
@@ -23,7 +22,7 @@ module ActiveSupport
end
def clear(options = nil)
- root_dirs = Dir.entries(cache_path).reject{|f| f.in?(EXCLUDED_DIRS + [".gitkeep"])}
+ root_dirs = Dir.entries(cache_path).reject {|f| (EXCLUDED_DIRS + [".gitkeep"]).include?(f)}
FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
end
@@ -151,7 +150,7 @@ module ActiveSupport
# Delete empty directories in the cache.
def delete_empty_directories(dir)
return if dir == cache_path
- if Dir.entries(dir).reject{|f| f.in?(EXCLUDED_DIRS)}.empty?
+ if Dir.entries(dir).reject {|f| EXCLUDED_DIRS.include?(f)}.empty?
File.delete(dir) rescue nil
delete_empty_directories(File.dirname(dir))
end
@@ -165,7 +164,7 @@ module ActiveSupport
def search_dir(dir, &callback)
return if !File.exist?(dir)
Dir.foreach(dir) do |d|
- next if d.in?(EXCLUDED_DIRS)
+ next if EXCLUDED_DIRS.include?(d)
name = File.join(dir, d)
if File.directory?(name)
search_dir(name, &callback)
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 2e1ccb72d8..5aa78cc9f3 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -1,7 +1,7 @@
begin
- require 'memcache'
+ require 'dalli'
rescue LoadError => e
- $stderr.puts "You don't have memcache-client installed in your application. Please add it to your Gemfile and run bundle install"
+ $stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
raise e
end
@@ -22,21 +22,13 @@ module ActiveSupport
# MemCacheStore implements the Strategy::LocalCache strategy which implements
# an in-memory cache inside of a block.
class MemCacheStore < Store
- module Response # :nodoc:
- STORED = "STORED\r\n"
- NOT_STORED = "NOT_STORED\r\n"
- EXISTS = "EXISTS\r\n"
- NOT_FOUND = "NOT_FOUND\r\n"
- DELETED = "DELETED\r\n"
- end
-
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
def self.build_mem_cache(*addresses)
addresses = addresses.flatten
options = addresses.extract_options!
addresses = ["localhost:11211"] if addresses.empty?
- MemCache.new(addresses, options)
+ Dalli::Client.new(addresses, options)
end
# Creates a new MemCacheStore object, with the given memcached server
@@ -90,11 +82,11 @@ module ActiveSupport
# to zero.
def increment(name, amount = 1, options = nil) # :nodoc:
options = merged_options(options)
- response = instrument(:increment, name, :amount => amount) do
+ instrument(:increment, name, :amount => amount) do
@data.incr(escape_key(namespaced_key(name, options)), amount)
end
- response == Response::NOT_FOUND ? nil : response.to_i
- rescue MemCache::MemCacheError
+ rescue Dalli::DalliError
+ logger.error("DalliError (#{e}): #{e.message}") if logger
nil
end
@@ -104,11 +96,11 @@ module ActiveSupport
# to zero.
def decrement(name, amount = 1, options = nil) # :nodoc:
options = merged_options(options)
- response = instrument(:decrement, name, :amount => amount) do
+ instrument(:decrement, name, :amount => amount) do
@data.decr(escape_key(namespaced_key(name, options)), amount)
end
- response == Response::NOT_FOUND ? nil : response.to_i
- rescue MemCache::MemCacheError
+ rescue Dalli::DalliError
+ logger.error("DalliError (#{e}): #{e.message}") if logger
nil
end
@@ -116,6 +108,9 @@ module ActiveSupport
# be used with care when shared cache is being used.
def clear(options = nil)
@data.flush_all
+ rescue Dalli::DalliError => e
+ logger.error("DalliError (#{e}): #{e.message}") if logger
+ nil
end
# Get the statistics from the memcached servers.
@@ -126,9 +121,9 @@ module ActiveSupport
protected
# Read an entry from the cache.
def read_entry(key, options) # :nodoc:
- deserialize_entry(@data.get(escape_key(key), true))
- rescue MemCache::MemCacheError => e
- logger.error("MemCacheError (#{e}): #{e.message}") if logger
+ deserialize_entry(@data.get(escape_key(key), options))
+ rescue Dalli::DalliError => e
+ logger.error("DalliError (#{e}): #{e.message}") if logger
nil
end
@@ -137,23 +132,17 @@ module ActiveSupport
method = options && options[:unless_exist] ? :add : :set
value = options[:raw] ? entry.value.to_s : entry
expires_in = options[:expires_in].to_i
- if expires_in > 0 && !options[:raw]
- # Set the memcache expire a few minutes in the future to support race condition ttls on read
- expires_in += 5.minutes
- end
- response = @data.send(method, escape_key(key), value, expires_in, options[:raw])
- response == Response::STORED
- rescue MemCache::MemCacheError => e
- logger.error("MemCacheError (#{e}): #{e.message}") if logger
+ @data.send(method, escape_key(key), value, expires_in, options)
+ rescue Dalli::DalliError => e
+ logger.error("DalliError (#{e}): #{e.message}") if logger
false
end
# Delete an entry from the cache.
def delete_entry(key, options) # :nodoc:
- response = @data.delete(escape_key(key))
- response == Response::DELETED
- rescue MemCache::MemCacheError => e
- logger.error("MemCacheError (#{e}): #{e.message}") if logger
+ @data.delete(escape_key(key))
+ rescue Dalli::DalliError => e
+ logger.error("DalliError (#{e}): #{e.message}") if logger
false
end
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 8f79334159..3f7d0e401a 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -3,7 +3,6 @@ require 'active_support/descendants_tracker'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/core_ext/object/inclusion'
module ActiveSupport
# \Callbacks are code hooks that are run at key points in an object's lifecycle.
@@ -353,7 +352,7 @@ module ActiveSupport
# CallbackChain.
#
def __update_callbacks(name, filters = [], block = nil) #:nodoc:
- type = filters.first.in?([:before, :after, :around]) ? filters.shift : :before
+ type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
options = filters.last.is_a?(Hash) ? filters.pop : {}
filters.unshift(block) if block
diff --git a/activesupport/lib/active_support/core_ext/file/atomic.rb b/activesupport/lib/active_support/core_ext/file/atomic.rb
index 9e504851e7..81beb4e85d 100644
--- a/activesupport/lib/active_support/core_ext/file/atomic.rb
+++ b/activesupport/lib/active_support/core_ext/file/atomic.rb
@@ -1,3 +1,5 @@
+require 'fileutils'
+
class File
# Write to a file atomically. Useful for situations where you don't
# want other processes or threads to see half-written files.
@@ -25,17 +27,9 @@ class File
# Get original file permissions
old_stat = stat(file_name)
rescue Errno::ENOENT
- # No old permissions, write a temp file to determine the defaults
- temp_file_name = [
- '.permissions_check',
- Thread.current.object_id,
- Process.pid,
- rand(1000000)
- ].join('.')
- check_name = join(dirname(file_name), temp_file_name)
- open(check_name, 'w') { }
- old_stat = stat(check_name)
- unlink(check_name)
+ # If not possible, probe which are the default permissions in the
+ # destination directory.
+ old_stat = probe_stat_in(dirname(file_name))
end
# Overwrite original file with temp file
@@ -45,4 +39,20 @@ class File
chown(old_stat.uid, old_stat.gid, file_name)
chmod(old_stat.mode, file_name)
end
+
+ # Private utility method.
+ def self.probe_stat_in(dir) #:nodoc:
+ basename = [
+ '.permissions_check',
+ Thread.current.object_id,
+ Process.pid,
+ rand(1000000)
+ ].join('.')
+
+ file_name = join(dir, basename)
+ FileUtils.touch(file_name)
+ stat(file_name)
+ ensure
+ FileUtils.rm_f(file_name) if file_name
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index 9974b61078..1079ddde98 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -37,7 +37,7 @@ class Object
public_send(*a, &b) if respond_to?(a.first)
end
end
-
+
# Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
# does not implemented the tried method.
def try!(*a, &b)
@@ -63,7 +63,7 @@ class NilClass
def try(*args)
nil
end
-
+
def try!(*args)
nil
end
diff --git a/activesupport/lib/active_support/core_ext/string.rb b/activesupport/lib/active_support/core_ext/string.rb
index fb36262528..ad864765a3 100644
--- a/activesupport/lib/active_support/core_ext/string.rb
+++ b/activesupport/lib/active_support/core_ext/string.rb
@@ -10,3 +10,4 @@ require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/string/exclude'
require 'active_support/core_ext/string/strip'
require 'active_support/core_ext/string/inquiry'
+require 'active_support/core_ext/string/indent'
diff --git a/activesupport/lib/active_support/core_ext/string/indent.rb b/activesupport/lib/active_support/core_ext/string/indent.rb
new file mode 100644
index 0000000000..afc3032272
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/string/indent.rb
@@ -0,0 +1,43 @@
+class String
+ # Same as +indent+, except it indents the receiver in-place.
+ #
+ # Returns the indented string, or +nil+ if there was nothing to indent.
+ def indent!(amount, indent_string=nil, indent_empty_lines=false)
+ indent_string = indent_string || self[/^[ \t]/] || ' '
+ re = indent_empty_lines ? /^/ : /^(?!$)/
+ gsub!(re, indent_string * amount)
+ end
+
+ # Indents the lines in the receiver:
+ #
+ # <<EOS.indent(2)
+ # def some_method
+ # some_code
+ # end
+ # EOS
+ # # =>
+ # def some_method
+ # some_code
+ # end
+ #
+ # The second argument, +indent_string+, specifies which indent string to
+ # use. The default is +nil+, which tells the method to make a guess by
+ # peeking at the first indented line, and fallback to a space if there is
+ # none.
+ #
+ # " foo".indent(2) # => " foo"
+ # "foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
+ # "foo".indent(2, "\t") # => "\t\tfoo"
+ #
+ # While +indent_string+ is tipically one space or tab, it may be any string.
+ #
+ # The third argument, +indent_empty_lines+, is a flag that says whether
+ # empty lines should be indented. Default is false.
+ #
+ # "foo\n\nbar".indent(2) # => " foo\n\n bar"
+ # "foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
+ #
+ def indent(amount, indent_string=nil, indent_empty_lines=false)
+ dup.tap {|_| _.indent!(amount, indent_string, indent_empty_lines)}
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index c17d695967..dad4b29d46 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -60,18 +60,11 @@ class ERB
# json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}')
# # => {name:john,created_at:2010-04-28T01:39:31Z,id:1}
#
- # This method is also aliased as +j+, and available as a helper
- # in Rails templates:
- #
- # <%=j @person.to_json %>
- #
def json_escape(s)
result = s.to_s.gsub(JSON_ESCAPE_REGEXP) { |special| JSON_ESCAPE[special] }
s.html_safe? ? result.html_safe : result
end
- alias j json_escape
- module_function :j
module_function :json_escape
end
end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index c9071a73d8..d78af2faa5 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -168,16 +168,30 @@ module ActiveSupport #:nodoc:
end
end
- def const_missing(const_name, nesting = nil)
+ def const_missing(const_name)
klass_name = name.presence || "Object"
- unless nesting
- # We'll assume that the nesting of Foo::Bar is ["Foo::Bar", "Foo"]
- # even though it might not be, such as in the case of
- # class Foo::Bar; Baz; end
- nesting = []
- klass_name.to_s.scan(/::|$/) { nesting.unshift $` }
- end
+ # Since Ruby does not pass the nesting at the point the unknown
+ # constant triggered the callback we cannot fully emulate constant
+ # name lookup and need to make a trade-off: we are going to assume
+ # that the nesting in the body of Foo::Bar is [Foo::Bar, Foo] even
+ # though it might not be. Counterexamples are
+ #
+ # class Foo::Bar
+ # Module.nesting # => [Foo::Bar]
+ # end
+ #
+ # or
+ #
+ # module M::N
+ # module S::T
+ # Module.nesting # => [S::T, M::N]
+ # end
+ # end
+ #
+ # for example.
+ nesting = []
+ klass_name.to_s.scan(/::|$/) { nesting.unshift $` }
# If there are multiple levels of nesting to search under, the top
# level is the one we want to report as the lookup fail.
@@ -287,13 +301,11 @@ module ActiveSupport #:nodoc:
Object.class_eval { include Loadable }
Module.class_eval { include ModuleConstMissing }
Exception.class_eval { include Blamable }
- true
end
def unhook!
ModuleConstMissing.exclude_from(Module)
Loadable.exclude_from(Object)
- true
end
def load?
@@ -319,7 +331,7 @@ module ActiveSupport #:nodoc:
def require_or_load(file_name, const_path = nil)
log_call file_name, const_path
- file_name = $1 if file_name =~ /^(.*)\.rb$/
+ file_name = $` if file_name =~ /\.rb\z/
expanded = File.expand_path(file_name)
return if loaded.include?(expanded)
@@ -363,7 +375,7 @@ module ActiveSupport #:nodoc:
# Given +path+, a filesystem path to a ruby file, return an array of constant
# paths which would cause Dependencies to attempt to load this file.
def loadable_constants_for_path(path, bases = autoload_paths)
- path = $1 if path =~ /\A(.*)\.rb\Z/
+ path = $` if path =~ /\.rb\z/
expanded_path = File.expand_path(path)
paths = []
@@ -431,7 +443,7 @@ module ActiveSupport #:nodoc:
def load_file(path, const_paths = loadable_constants_for_path(path))
log_call path, const_paths
const_paths = [const_paths].compact unless const_paths.is_a? Array
- parent_paths = const_paths.collect { |const_path| /(.*)::[^:]+\Z/ =~ const_path ? $1 : :Object }
+ parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || :Object }
result = nil
newly_defined_paths = new_constants_in(*parent_paths) do
diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb
index 4045db3232..df490ae298 100644
--- a/activesupport/lib/active_support/dependencies/autoload.rb
+++ b/activesupport/lib/active_support/dependencies/autoload.rb
@@ -1,52 +1,78 @@
require "active_support/inflector/methods"
module ActiveSupport
+ # Autoload and eager load conveniences for your library.
+ #
+ # This module allows you to define autoloads based on
+ # Rails conventions (i.e. no need to define the path
+ # it is automatically guessed based on the filename)
+ # and also define a set of constants that needs to be
+ # eager loaded:
+ #
+ # module MyLib
+ # extend ActiveSupport::Autoload
+ #
+ # autoload :Model
+ #
+ # eager_autoload do
+ # autoload :Cache
+ # end
+ # end
+ #
+ # Then your library can be eager loaded by simply calling:
+ #
+ # MyLib.eager_load!
+ #
module Autoload
- @@autoloads = {}
- @@under_path = nil
- @@at_path = nil
- @@eager_autoload = false
+ def self.extended(base)
+ base.class_eval do
+ @_autoloads = {}
+ @_under_path = nil
+ @_at_path = nil
+ @_eager_autoload = false
+ end
+ end
- def autoload(const_name, path = @@at_path)
+ def autoload(const_name, path = @_at_path)
unless path
- full = [name, @@under_path, const_name.to_s, path].compact.join("::")
+ full = [name, @_under_path, const_name.to_s, path].compact.join("::")
path = Inflector.underscore(full)
end
- if @@eager_autoload
- @@autoloads[const_name] = path
+ if @_eager_autoload
+ @_autoloads[const_name] = path
end
super const_name, path
end
def autoload_under(path)
- @@under_path, old_path = path, @@under_path
+ @_under_path, old_path = path, @_under_path
yield
ensure
- @@under_path = old_path
+ @_under_path = old_path
end
def autoload_at(path)
- @@at_path, old_path = path, @@at_path
+ @_at_path, old_path = path, @_at_path
yield
ensure
- @@at_path = old_path
+ @_at_path = old_path
end
def eager_autoload
- old_eager, @@eager_autoload = @@eager_autoload, true
+ old_eager, @_eager_autoload = @_eager_autoload, true
yield
ensure
- @@eager_autoload = old_eager
+ @_eager_autoload = old_eager
end
- def self.eager_autoload!
- @@autoloads.values.each { |file| require file }
+ def eager_load!
+ @_autoloads.values.each { |file| require file }
end
def autoloads
- @@autoloads
+ @_autoloads
end
end
end
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 2cdc991120..a0139b7d8e 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -70,7 +70,7 @@ module ActiveSupport
alias :until :ago
def inspect #:nodoc:
- consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h }
+ consolidated = parts.inject(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }
parts = [:years, :months, :days, :minutes, :seconds].map do |length|
n = consolidated[length]
"#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index 389df58ec4..1a95bd63e6 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/object/to_json'
require 'active_support/core_ext/module/delegation'
+require 'active_support/json/variable'
require 'bigdecimal'
require 'active_support/core_ext/big_decimal/conversions' # for #to_s
diff --git a/activesupport/lib/active_support/json/variable.rb b/activesupport/lib/active_support/json/variable.rb
new file mode 100644
index 0000000000..8af661a795
--- /dev/null
+++ b/activesupport/lib/active_support/json/variable.rb
@@ -0,0 +1,17 @@
+require 'active_support/deprecation'
+
+module ActiveSupport
+ module JSON
+ # Deprecated: A string that returns itself as its JSON-encoded form.
+ class Variable < String
+ def initialize(*args)
+ ActiveSupport::Deprecation.warn 'ActiveSupport::JSON::Variable is deprecated and will be removed in Rails 4.1. ' \
+ 'For your own custom JSON literals, define #as_json and #encode_json yourself.'
+ super
+ end
+
+ def as_json(options = nil) self end #:nodoc:
+ def encode_json(encoder) self end #:nodoc:
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/locale/en.yml b/activesupport/lib/active_support/locale/en.yml
index 18c7d47026..f4900dc935 100644
--- a/activesupport/lib/active_support/locale/en.yml
+++ b/activesupport/lib/active_support/locale/en.yml
@@ -49,7 +49,7 @@ en:
significant: false
# If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
strip_insignificant_zeros: false
-
+
# Used in NumberHelper.number_to_currency()
currency:
format:
@@ -62,7 +62,7 @@ en:
precision: 2
significant: false
strip_insignificant_zeros: false
-
+
# Used in NumberHelper.number_to_percentage()
percentage:
format:
@@ -73,7 +73,7 @@ en:
# significant: false
# strip_insignificant_zeros: false
format: "%n%"
-
+
# Used in NumberHelper.number_to_rounded()
precision:
format:
@@ -83,7 +83,7 @@ en:
# precision:
# significant: false
# strip_insignificant_zeros: false
-
+
# Used in NumberHelper.number_to_human_size() and NumberHelper.number_to_human()
human:
format:
@@ -131,5 +131,3 @@ en:
billion: Billion
trillion: Trillion
quadrillion: Quadrillion
-
- \ No newline at end of file
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index 6ffc091233..2e5bcf4639 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -59,10 +59,10 @@ module ActiveSupport
module Subscribers # :nodoc:
def self.new(pattern, listener)
- if listener.respond_to?(:call)
- subscriber = Timed.new pattern, listener
- else
+ if listener.respond_to?(:start) and listener.respond_to?(:finish)
subscriber = Evented.new pattern, listener
+ else
+ subscriber = Timed.new pattern, listener
end
unless pattern
diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb
index 99f6489adb..3849f94a31 100644
--- a/activesupport/lib/active_support/number_helper.rb
+++ b/activesupport/lib/active_support/number_helper.rb
@@ -7,12 +7,108 @@ module ActiveSupport
module NumberHelper
extend self
+ DEFAULTS = {
+ # Used in number_to_delimited
+ # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
+ format: {
+ # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
+ separator: ".",
+ # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
+ delimiter: ",",
+ # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
+ precision: 3,
+ # If set to true, precision will mean the number of significant digits instead
+ # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
+ significant: false,
+ # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
+ strip_insignificant_zeros: false
+ },
+
+ # Used in number_to_currency
+ currency: {
+ format: {
+ format: "%u%n",
+ negative_format: "-%u%n",
+ unit: "$",
+ # These five are to override number.format and are optional
+ separator: ".",
+ delimiter: ",",
+ precision: 2,
+ significant: false,
+ strip_insignificant_zeros: false
+ }
+ },
+
+ # Used in number_to_percentage
+ percentage: {
+ format: {
+ delimiter: "",
+ format: "%n%"
+ }
+ },
+
+ # Used in number_to_rounded
+ precision: {
+ format: {
+ delimiter: ""
+ }
+ },
+
+ # Used in number_to_human_size and number_to_human
+ human: {
+ format: {
+ # These five are to override number.format and are optional
+ delimiter: "",
+ precision: 3,
+ significant: true,
+ strip_insignificant_zeros: true
+ },
+ # Used in number_to_human_size
+ storage_units: {
+ # Storage units output formatting.
+ # %u is the storage unit, %n is the number (default: 2 MB)
+ format: "%n %u",
+ units: {
+ byte: "Bytes",
+ kb: "KB",
+ mb: "MB",
+ gb: "GB",
+ tb: "TB"
+ }
+ },
+ # Used in number_to_human
+ decimal_units: {
+ format: "%n %u",
+ # Decimal units output formatting
+ # By default we will only quantify some of the exponents
+ # but the commented ones might be defined or overridden
+ # by the user.
+ units: {
+ # femto: Quadrillionth
+ # pico: Trillionth
+ # nano: Billionth
+ # micro: Millionth
+ # mili: Thousandth
+ # centi: Hundredth
+ # deci: Tenth
+ unit: "",
+ # ten:
+ # one: Ten
+ # other: Tens
+ # hundred: Hundred
+ thousand: "Thousand",
+ million: "Million",
+ billion: "Billion",
+ trillion: "Trillion",
+ quadrillion: "Quadrillion"
+ }
+ }
+ }
+ }
+
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 }
- DEFAULT_CURRENCY_VALUES = { :format => "%u%n", :negative_format => "-%u%n", :unit => "$", :separator => ".", :delimiter => ",",
- :precision => 2, :significant => false, :strip_insignificant_zeros => false }
-
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
# Formats a +number+ into a US phone number (e.g., (555)
@@ -106,10 +202,10 @@ module ActiveSupport
return unless number
options = options.symbolize_keys
- currency = translations_for('currency', options[:locale])
+ currency = i18n_format_options(options[:locale], :currency)
currency[:negative_format] ||= "-" + currency[:format] if currency[:format]
- defaults = DEFAULT_CURRENCY_VALUES.merge(defaults_translations(options[:locale])).merge!(currency)
+ defaults = default_format_options(:currency).merge!(currency)
defaults[:negative_format] = "-" + options[:format] if options[:format]
options = defaults.merge!(options)
@@ -160,7 +256,7 @@ module ActiveSupport
return unless number
options = options.symbolize_keys
- defaults = format_translations('percentage', options[:locale])
+ defaults = format_options(options[:locale], :percentage)
options = defaults.merge!(options)
format = options[:format] || "%n%"
@@ -197,7 +293,7 @@ module ActiveSupport
return number unless valid_float?(number)
- options = defaults_translations(options[:locale]).merge(options)
+ options = format_options(options[:locale]).merge!(options)
parts = number.to_s.to_str.split('.')
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
@@ -248,7 +344,7 @@ module ActiveSupport
number = Float(number)
options = options.symbolize_keys
- defaults = format_translations('precision', options[:locale])
+ defaults = format_options(options[:locale], :precision)
options = defaults.merge!(options)
precision = options.delete :precision
@@ -328,18 +424,18 @@ module ActiveSupport
return number unless valid_float?(number)
number = Float(number)
- defaults = format_translations('human', options[:locale])
+ defaults = format_options(options[:locale], :human)
options = defaults.merge!(options)
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
- storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true)
+ storage_units_format = translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
base = options[:prefix] == :si ? 1000 : 1024
if number.to_i < base
- unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
+ unit = translate_number_value_with_default('human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
else
max_exp = STORAGE_UNITS.size - 1
@@ -348,7 +444,7 @@ module ActiveSupport
number /= base ** exponent
unit_key = STORAGE_UNITS[exponent]
- unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
+ unit = translate_number_value_with_default("human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
formatted_number = self.number_to_rounded(number, options)
storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
@@ -458,7 +554,7 @@ module ActiveSupport
return number unless valid_float?(number)
number = Float(number)
- defaults = format_translations('human', options[:locale])
+ defaults = format_options(options[:locale], :human)
options = defaults.merge!(options)
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
@@ -473,7 +569,7 @@ module ActiveSupport
when String, Symbol
I18n.translate(:"#{units}", :locale => options[:locale], :raise => true)
when nil
- I18n.translate(:"number.human.decimal_units.units", :locale => options[:locale], :raise => true)
+ translate_number_value_with_default("human.decimal_units.units", :locale => options[:locale], :raise => true)
else
raise ArgumentError, ":units must be a Hash or String translation scope."
end.keys.map{|e_name| inverted_du[e_name] }.sort_by{|e| -e}
@@ -488,34 +584,47 @@ module ActiveSupport
when String, Symbol
I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
else
- I18n.translate(:"number.human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
+ translate_number_value_with_default("human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
end
- decimal_format = options[:format] || I18n.translate(:'number.human.decimal_units.format', :locale => options[:locale], :default => "%n %u")
+ decimal_format = options[:format] || translate_number_value_with_default('human.decimal_units.format', :locale => options[:locale])
formatted_number = self.number_to_rounded(number, options)
decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip
end
- def self.private_module_and_instance_method(method_name)
+ def self.private_module_and_instance_method(method_name) #:nodoc:
private method_name
private_class_method method_name
end
private_class_method :private_module_and_instance_method
- def format_translations(namespace, locale) #:nodoc:
- defaults_translations(locale).merge(translations_for(namespace, locale))
+ def format_options(locale, namespace = nil) #:nodoc:
+ default_format_options(namespace).merge!(i18n_format_options(locale, namespace))
+ end
+ private_module_and_instance_method :format_options
+
+ def default_format_options(namespace = nil) #:nodoc:
+ options = DEFAULTS[:format].dup
+ options.merge!(DEFAULTS[namespace][:format]) if namespace
+ options
end
- private_module_and_instance_method :format_translations
+ private_module_and_instance_method :default_format_options
- def defaults_translations(locale) #:nodoc:
- I18n.translate(:'number.format', :locale => locale, :default => {})
+ def i18n_format_options(locale, namespace = nil) #:nodoc:
+ options = I18n.translate(:'number.format', locale: locale, default: {}).dup
+ if namespace
+ options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
+ end
+ options
end
- private_module_and_instance_method :defaults_translations
+ private_module_and_instance_method :i18n_format_options
+
+ def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
+ default = key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
- def translations_for(namespace, locale) #:nodoc:
- I18n.translate(:"number.#{namespace}.format", :locale => locale, :default => {})
+ I18n.translate(key, { default: default, scope: :number }.merge!(i18n_options))
end
- private_module_and_instance_method :translations_for
+ private_module_and_instance_method :translate_number_value_with_default
def valid_float?(number) #:nodoc:
Float(number)
diff --git a/activesupport/lib/active_support/rails.rb b/activesupport/lib/active_support/rails.rb
index 933c6e6136..b05c3ff126 100644
--- a/activesupport/lib/active_support/rails.rb
+++ b/activesupport/lib/active_support/rails.rb
@@ -11,9 +11,6 @@
# Defines Object#blank? and Object#present?.
require 'active_support/core_ext/object/blank'
-# Defines Object#in?.
-require 'active_support/core_ext/object/inclusion'
-
# Rails own autoload, eager_load, etc.
require 'active_support/dependencies/autoload'
diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb
index 30ac881090..aa8d408da9 100644
--- a/activesupport/lib/active_support/railtie.rb
+++ b/activesupport/lib/active_support/railtie.rb
@@ -5,6 +5,8 @@ module ActiveSupport
class Railtie < Rails::Railtie
config.active_support = ActiveSupport::OrderedOptions.new
+ config.eager_load_namespaces << ActiveSupport
+
initializer "active_support.deprecation_behavior" do |app|
if deprecation = app.config.active_support.deprecation
ActiveSupport::Deprecation.behavior = deprecation
diff --git a/activesupport/lib/active_support/string_inquirer.rb b/activesupport/lib/active_support/string_inquirer.rb
index f3f3909a90..5f20bfa7bc 100644
--- a/activesupport/lib/active_support/string_inquirer.rb
+++ b/activesupport/lib/active_support/string_inquirer.rb
@@ -10,12 +10,18 @@ module ActiveSupport
# Rails.env.production?
#
class StringInquirer < String
- def method_missing(method_name, *arguments)
- if method_name[-1, 1] == "?"
- self == method_name[0..-2]
- else
- super
+ private
+
+ def respond_to_missing?(method_name, include_private = false)
+ method_name[-1] == '?'
+ end
+
+ def method_missing(method_name, *arguments)
+ if method_name[-1] == '?'
+ self == method_name[0..-2]
+ else
+ super
+ end
end
- end
end
end
diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb
index 9cd8ac36bc..5e080df518 100644
--- a/activesupport/lib/active_support/tagged_logging.rb
+++ b/activesupport/lib/active_support/tagged_logging.rb
@@ -16,7 +16,7 @@ module ActiveSupport
module Formatter # :nodoc:
# This method is invoked when a log event occurs
def call(severity, timestamp, progname, msg)
- super(severity, timestamp, progname, "#{tags_text} #{msg}".lstrip)
+ super(severity, timestamp, progname, "#{tags_text}#{msg}")
end
def clear!
@@ -31,7 +31,7 @@ module ActiveSupport
def tags_text
tags = current_tags
if tags.any?
- tags.collect { |tag| "[#{tag}]" }.join(' ')
+ tags.collect { |tag| "[#{tag}] " }.join
end
end
end
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 451520ac5c..93c2d614f5 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -1,6 +1,5 @@
require 'active_support/values/time_zone'
require 'active_support/core_ext/object/acts_like'
-require 'active_support/core_ext/object/inclusion'
module ActiveSupport
# A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are
@@ -339,7 +338,7 @@ module ActiveSupport
end
def duration_of_variable_length?(obj)
- ActiveSupport::Duration === obj && obj.parts.any? {|p| p[0].in?([:years, :months, :days]) }
+ ActiveSupport::Duration === obj && obj.parts.any? {|p| [:years, :months, :days].include?(p[0]) }
end
def wrap_with_time_zone(time)
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index 25ed962c23..8a67b148c3 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -21,15 +21,5 @@ require 'empty_bool'
ENV['NO_RELOAD'] = '1'
require 'active_support'
-def uses_memcached(test_name)
- require 'memcache'
- begin
- MemCache.new('localhost:11211').stats
- yield
- rescue MemCache::MemCacheError
- $stderr.puts "Skipping #{test_name} tests. Start memcached and try again."
- end
-end
-
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
diff --git a/activesupport/test/autoload.rb b/activesupport/test/autoload_test.rb
index 5d8026a9ca..7d02d835a8 100644
--- a/activesupport/test/autoload.rb
+++ b/activesupport/test/autoload_test.rb
@@ -28,15 +28,6 @@ class TestAutoloadModule < ActiveSupport::TestCase
assert_nothing_raised { ::Fixtures::Autoload::SomeClass }
end
- test ":eager constants can be triggered via ActiveSupport::Autoload.eager_autoload!" do
- module ::Fixtures::Autoload
- autoload :SomeClass, "fixtures/autoload/some_class"
- end
- ActiveSupport::Autoload.eager_autoload!
- assert $LOADED_FEATURES.include?("fixtures/autoload/some_class.rb")
- assert_nothing_raised { ::Fixtures::Autoload::SomeClass }
- end
-
test "the location of autoloaded constants defaults to :name.underscore" do
module ::Fixtures::Autoload
autoload :SomeClass
@@ -51,8 +42,7 @@ class TestAutoloadModule < ActiveSupport::TestCase
autoload :SomeClass
end
- ActiveSupport::Autoload.eager_autoload!
- assert $LOADED_FEATURES.include?("fixtures/autoload/some_class.rb")
+ ::Fixtures::Autoload.eager_load!
assert_nothing_raised { ::Fixtures::Autoload::SomeClass }
end
diff --git a/activesupport/test/autoloading_fixtures/class_folder/class_folder_subclass.rb b/activesupport/test/autoloading_fixtures/class_folder/class_folder_subclass.rb
index ef66ddaac7..402609c583 100644
--- a/activesupport/test/autoloading_fixtures/class_folder/class_folder_subclass.rb
+++ b/activesupport/test/autoloading_fixtures/class_folder/class_folder_subclass.rb
@@ -1,3 +1,3 @@
class ClassFolder::ClassFolderSubclass < ClassFolder
- ConstantInClassFolder
+ ConstantInClassFolder = 'indeed'
end
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index a75db47be8..71cd9d81b3 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -83,20 +83,20 @@ class CacheStoreSettingTest < ActiveSupport::TestCase
end
def test_mem_cache_fragment_cache_store
- MemCache.expects(:new).with(%w[localhost], {})
+ Dalli::Client.expects(:new).with(%w[localhost], {})
store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost"
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
end
def test_mem_cache_fragment_cache_store_with_given_mem_cache
- mem_cache = MemCache.new
- MemCache.expects(:new).never
+ mem_cache = Dalli::Client.new
+ Dalli::Client.expects(:new).never
store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
end
def test_mem_cache_fragment_cache_store_with_given_mem_cache_like_object
- MemCache.expects(:new).never
+ Dalli::Client.expects(:new).never
memcache = Object.new
def memcache.get() true end
store = ActiveSupport::Cache.lookup_store :mem_cache_store, memcache
@@ -104,13 +104,13 @@ class CacheStoreSettingTest < ActiveSupport::TestCase
end
def test_mem_cache_fragment_cache_store_with_multiple_servers
- MemCache.expects(:new).with(%w[localhost 192.168.1.1], {})
+ Dalli::Client.expects(:new).with(%w[localhost 192.168.1.1], {})
store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1'
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
end
def test_mem_cache_fragment_cache_store_with_options
- MemCache.expects(:new).with(%w[localhost 192.168.1.1], { :timeout => 10 })
+ Dalli::Client.expects(:new).with(%w[localhost 192.168.1.1], { :timeout => 10 })
store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo', :timeout => 10
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
assert_equal 'foo', store.options[:namespace]
@@ -447,6 +447,7 @@ module CacheIncrementDecrementBehavior
assert_equal 2, @cache.read('foo').to_i
assert_equal 3, @cache.increment('foo')
assert_equal 3, @cache.read('foo').to_i
+ assert_nil @cache.increment('bar')
end
def test_decrement
@@ -456,6 +457,7 @@ module CacheIncrementDecrementBehavior
assert_equal 2, @cache.read('foo').to_i
assert_equal 1, @cache.decrement('foo')
assert_equal 1, @cache.read('foo').to_i
+ assert_nil @cache.decrement('bar')
end
end
@@ -702,53 +704,65 @@ class MemoryStoreTest < ActiveSupport::TestCase
end
end
-uses_memcached 'memcached backed store' do
- class MemCacheStoreTest < ActiveSupport::TestCase
- def setup
- @cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :expires_in => 60)
- @peek = ActiveSupport::Cache.lookup_store(:mem_cache_store)
- @data = @cache.instance_variable_get(:@data)
- @cache.clear
- @cache.silence!
- @cache.logger = ActiveSupport::Logger.new("/dev/null")
- end
+class MemCacheStoreTest < ActiveSupport::TestCase
+ require 'dalli'
+
+ begin
+ ss = Dalli::Client.new('localhost:11211').stats
+ raise Dalli::DalliError unless ss['localhost:11211']
+
+ MEMCACHE_UP = true
+ rescue Dalli::DalliError
+ $stderr.puts "Skipping memcached tests. Start memcached and try again."
+ MEMCACHE_UP = false
+ end
+
+ def setup
+ skip "memcache server is not up" unless MEMCACHE_UP
+
+ @cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :expires_in => 60)
+ @peek = ActiveSupport::Cache.lookup_store(:mem_cache_store)
+ @data = @cache.instance_variable_get(:@data)
+ @cache.clear
+ @cache.silence!
+ @cache.logger = ActiveSupport::Logger.new("/dev/null")
+ end
+
+ include CacheStoreBehavior
+ include LocalCacheBehavior
+ include CacheIncrementDecrementBehavior
+ include EncodedKeyCacheBehavior
+
+ def test_raw_values
+ cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
+ cache.clear
+ cache.write("foo", 2)
+ assert_equal "2", cache.read("foo")
+ end
- include CacheStoreBehavior
- include LocalCacheBehavior
- include CacheIncrementDecrementBehavior
- include EncodedKeyCacheBehavior
+ def test_raw_values_with_marshal
+ cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
+ cache.clear
+ cache.write("foo", Marshal.dump([]))
+ assert_equal [], cache.read("foo")
+ end
- def test_raw_values
- cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
- cache.clear
+ def test_local_cache_raw_values
+ cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
+ cache.clear
+ cache.with_local_cache do
cache.write("foo", 2)
assert_equal "2", cache.read("foo")
end
+ end
- def test_raw_values_with_marshal
- cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
- cache.clear
+ def test_local_cache_raw_values_with_marshal
+ cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
+ cache.clear
+ cache.with_local_cache do
cache.write("foo", Marshal.dump([]))
assert_equal [], cache.read("foo")
end
-
- def test_local_cache_raw_values
- cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
- cache.clear
- cache.with_local_cache do
- cache.write("foo", 2)
- assert_equal "2", cache.read("foo")
- end
- end
-
- def test_local_cache_raw_values_with_marshal
- cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
- cache.clear
- cache.with_local_cache do
- cache.write("foo", Marshal.dump([]))
- assert_equal [], cache.read("foo")
- end
- end
end
end
diff --git a/activesupport/test/core_ext/file_test.rb b/activesupport/test/core_ext/file_test.rb
index 128e956a8c..2c04e9687c 100644
--- a/activesupport/test/core_ext/file_test.rb
+++ b/activesupport/test/core_ext/file_test.rb
@@ -51,7 +51,7 @@ class AtomicWriteTest < ActiveSupport::TestCase
assert !File.exist?(file_name)
end
assert File.exist?(file_name)
- assert_equal 0100666 & ~File.umask, file_mode
+ assert_equal File.probe_stat_in(Dir.pwd).mode, file_mode
assert_equal contents, File.read(file_name)
ensure
File.unlink(file_name) rescue nil
diff --git a/activesupport/test/core_ext/module/qualified_const_test.rb b/activesupport/test/core_ext/module/qualified_const_test.rb
index 8af0b9a023..343a848a42 100644
--- a/activesupport/test/core_ext/module/qualified_const_test.rb
+++ b/activesupport/test/core_ext/module/qualified_const_test.rb
@@ -67,17 +67,24 @@ class QualifiedConstTest < ActiveSupport::TestCase
end
test "qualified_const_set" do
- m = Module.new
- assert_equal m, Object.qualified_const_set("QualifiedConstTestMod2", m)
- assert_equal m, ::QualifiedConstTestMod2
-
- # We are going to assign to existing constants on purpose, so silence warnings.
- silence_warnings do
- assert_equal true, QualifiedConstTestMod.qualified_const_set("QualifiedConstTestMod::X", true)
- assert_equal true, QualifiedConstTestMod::X
-
- assert_equal 10, QualifiedConstTestMod::M.qualified_const_set("X", 10)
- assert_equal 10, QualifiedConstTestMod::M::X
+ begin
+ m = Module.new
+ assert_equal m, Object.qualified_const_set("QualifiedConstTestMod2", m)
+ assert_equal m, ::QualifiedConstTestMod2
+
+ # We are going to assign to existing constants on purpose, so silence warnings.
+ silence_warnings do
+ assert_equal true, QualifiedConstTestMod.qualified_const_set("QualifiedConstTestMod::X", true)
+ assert_equal true, QualifiedConstTestMod::X
+
+ assert_equal 10, QualifiedConstTestMod::M.qualified_const_set("X", 10)
+ assert_equal 10, QualifiedConstTestMod::M::X
+ end
+ ensure
+ silence_warnings do
+ QualifiedConstTestMod.qualified_const_set('QualifiedConstTestMod::X', false)
+ QualifiedConstTestMod::M.qualified_const_set('X', 1)
+ end
end
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 3b08ebe35f..dc5ae0eafc 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -9,6 +9,7 @@ require 'active_support/core_ext/string'
require 'active_support/time'
require 'active_support/core_ext/string/strip'
require 'active_support/core_ext/string/output_safety'
+require 'active_support/core_ext/string/indent'
module Ace
module Base
@@ -521,3 +522,58 @@ class StringExcludeTest < ActiveSupport::TestCase
assert_equal true, 'foo'.exclude?('p')
end
end
+
+class StringIndentTest < ActiveSupport::TestCase
+ test 'does not indent strings that only contain newlines (edge cases)' do
+ ['', "\n", "\n" * 7].each do |str|
+ assert_nil str.indent!(8)
+ assert_equal str, str.indent(8)
+ assert_equal str, str.indent(1, "\t")
+ end
+ end
+
+ test "by default, indents with spaces if the existing indentation uses them" do
+ assert_equal " foo\n bar", "foo\n bar".indent(4)
+ end
+
+ test "by default, indents with tabs if the existing indentation uses them" do
+ assert_equal "\tfoo\n\t\t\bar", "foo\n\t\bar".indent(1)
+ end
+
+ test "by default, indents with spaces as a fallback if there is no indentation" do
+ assert_equal " foo\n bar\n baz", "foo\nbar\nbaz".indent(3)
+ end
+
+ # Nothing is said about existing indentation that mixes spaces and tabs, so
+ # there is nothing to test.
+
+ test 'uses the indent char if passed' do
+ assert_equal <<EXPECTED, <<ACTUAL.indent(4, '.')
+.... def some_method(x, y)
+.... some_code
+.... end
+EXPECTED
+ def some_method(x, y)
+ some_code
+ end
+ACTUAL
+
+ assert_equal <<EXPECTED, <<ACTUAL.indent(2, '&nbsp;')
+&nbsp;&nbsp;&nbsp;&nbsp;def some_method(x, y)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;some_code
+&nbsp;&nbsp;&nbsp;&nbsp;end
+EXPECTED
+&nbsp;&nbsp;def some_method(x, y)
+&nbsp;&nbsp;&nbsp;&nbsp;some_code
+&nbsp;&nbsp;end
+ACTUAL
+ end
+
+ test "does not indent blank lines by default" do
+ assert_equal " foo\n\n bar", "foo\n\nbar".indent(1)
+ end
+
+ test 'indents blank lines if told so' do
+ assert_equal " foo\n \n bar", "foo\n\nbar".indent(1, nil, true)
+ end
+end
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index 69829bcda5..f1da6378a7 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -679,6 +679,8 @@ class DependenciesTest < ActiveSupport::TestCase
assert_equal true, M.unloadable
assert_equal false, M.unloadable
end
+ ensure
+ Object.class_eval { remove_const :M }
end
def test_unloadable_constants_should_receive_callback
diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb
index cd91002147..aa41e57928 100644
--- a/activesupport/test/inflector_test.rb
+++ b/activesupport/test/inflector_test.rb
@@ -169,11 +169,11 @@ class InflectorTest < ActiveSupport::TestCase
def test_underscore_acronym_sequence
ActiveSupport::Inflector.inflections do |inflect|
inflect.acronym("API")
- inflect.acronym("HTML5")
+ inflect.acronym("JSON")
inflect.acronym("HTML")
end
- assert_equal("html5_html_api", ActiveSupport::Inflector.underscore("HTML5HTMLAPI"))
+ assert_equal("json_html_api", ActiveSupport::Inflector.underscore("JSONHTMLAPI"))
end
def test_underscore
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index a947635f4a..7ed71f9abc 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -85,6 +85,13 @@ class TestJSONEncoding < ActiveSupport::TestCase
end
end
+ def test_json_variable
+ assert_deprecated do
+ assert_equal ActiveSupport::JSON::Variable.new('foo'), 'foo'
+ assert_equal ActiveSupport::JSON::Variable.new('alert("foo")'), 'alert("foo")'
+ end
+ end
+
def test_hash_encoding
assert_equal %({\"a\":\"b\"}), ActiveSupport::JSON.encode(:a => :b)
assert_equal %({\"a\":1}), ActiveSupport::JSON.encode('a' => 1)
diff --git a/activesupport/test/notifications/evented_notification_test.rb b/activesupport/test/notifications/evented_notification_test.rb
index f77a0eb3fa..f690ad43fc 100644
--- a/activesupport/test/notifications/evented_notification_test.rb
+++ b/activesupport/test/notifications/evented_notification_test.rb
@@ -19,6 +19,12 @@ module ActiveSupport
end
end
+ class ListenerWithTimedSupport < Listener
+ def call(name, start, finish, id, payload)
+ @events << [:call, name, start, finish, id, payload]
+ end
+ end
+
def test_evented_listener
notifier = Fanout.new
listener = Listener.new
@@ -62,6 +68,20 @@ module ActiveSupport
[:finish, 'hello', 1, {}],
], listener.events
end
+
+ def test_evented_listener_priority
+ notifier = Fanout.new
+ listener = ListenerWithTimedSupport.new
+ notifier.subscribe 'hi', listener
+
+ notifier.start 'hi', 1, {}
+ notifier.finish 'hi', 1, {}
+
+ assert_equal [
+ [:start, 'hi', 1, {}],
+ [:finish, 'hi', 1, {}]
+ ], listener.events
+ end
end
end
end
diff --git a/activesupport/test/number_helper_i18n_test.rb b/activesupport/test/number_helper_i18n_test.rb
index e07198027b..65aecece71 100644
--- a/activesupport/test/number_helper_i18n_test.rb
+++ b/activesupport/test/number_helper_i18n_test.rb
@@ -56,6 +56,13 @@ module ActiveSupport
assert_equal("-$10.00", number_to_currency(-10, :locale => 'empty'))
end
+ def test_locale_default_format_has_precedence_over_helper_defaults
+ I18n.backend.store_translations 'ts',
+ { :number => { :format => { :separator => ";" } } }
+
+ assert_equal("&$ - 10;00", number_to_currency(10, :locale => 'ts'))
+ end
+
def test_number_to_currency_without_currency_negative_format
I18n.backend.store_translations 'no_negative_format', :number => {
:currency => { :format => { :unit => '@', :format => '%n %u' } }
@@ -72,11 +79,24 @@ module ActiveSupport
assert_equal("1.00", number_to_rounded(1.0, :locale => 'ts'))
end
+ def test_number_with_i18n_precision_and_empty_i18n_store
+ I18n.backend.store_translations 'empty', {}
+
+ assert_equal("123456789.123", number_to_rounded(123456789.123456789, :locale => 'empty'))
+ assert_equal("1.000", number_to_rounded(1.0000, :locale => 'empty'))
+ end
+
def test_number_with_i18n_delimiter
#Delimiter "," and separator "."
assert_equal("1,000,000.234", number_to_delimited(1000000.234, :locale => 'ts'))
end
+ def test_number_with_i18n_delimiter_and_empty_i18n_store
+ I18n.backend.store_translations 'empty', {}
+
+ assert_equal("1,000,000.234", number_to_delimited(1000000.234, :locale => 'empty'))
+ end
+
def test_number_to_i18n_percentage
# to see if strip_insignificant_zeros is true
assert_equal("1%", number_to_percentage(1, :locale => 'ts'))
@@ -86,12 +106,27 @@ module ActiveSupport
assert_equal("12434%", number_to_percentage(12434, :locale => 'ts'))
end
+ def test_number_to_i18n_percentage_and_empty_i18n_store
+ I18n.backend.store_translations 'empty', {}
+
+ assert_equal("1.000%", number_to_percentage(1, :locale => 'empty'))
+ assert_equal("1.243%", number_to_percentage(1.2434, :locale => 'empty'))
+ assert_equal("12434.000%", number_to_percentage(12434, :locale => 'empty'))
+ end
+
def test_number_to_i18n_human_size
#b for bytes and k for kbytes
assert_equal("2 k", number_to_human_size(2048, :locale => 'ts'))
assert_equal("42 b", number_to_human_size(42, :locale => 'ts'))
end
+ def test_number_to_i18n_human_size_with_empty_i18n_store
+ I18n.backend.store_translations 'empty', {}
+
+ assert_equal("2 KB", number_to_human_size(2048, :locale => 'empty'))
+ assert_equal("42 Bytes", number_to_human_size(42, :locale => 'empty'))
+ end
+
def test_number_to_human_with_default_translation_scope
#Using t for thousand
assert_equal "2 t", number_to_human(2000, :locale => 'ts')
@@ -106,6 +141,13 @@ module ActiveSupport
assert_equal "2 Tens", number_to_human(20, :locale => 'ts')
end
+ def test_number_to_human_with_empty_i18n_store
+ I18n.backend.store_translations 'empty', {}
+
+ assert_equal "2 Thousand", number_to_human(2000, :locale => 'empty')
+ assert_equal "1.23 Billion", number_to_human(1234567890, :locale => 'empty')
+ end
+
def test_number_to_human_with_custom_translation_scope
#Significant was set to true with precision 2, with custom translated units
assert_equal "4.3 cm", number_to_human(0.0432, :locale => 'ts', :units => :custom_units_for_number_to_human)
diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb
index 9b7d7f020c..5f54587f93 100644
--- a/activesupport/test/number_helper_test.rb
+++ b/activesupport/test/number_helper_test.rb
@@ -4,7 +4,7 @@ require 'active_support/number_helper'
module ActiveSupport
module NumberHelper
class NumberHelperTest < ActiveSupport::TestCase
-
+
class TestClassWithInstanceNumberHelpers
include ActiveSupport::NumberHelper
end
@@ -16,7 +16,7 @@ module ActiveSupport
def setup
@instance_with_helpers = TestClassWithInstanceNumberHelpers.new
end
-
+
def kilobytes(number)
number * 1024
end
@@ -362,14 +362,13 @@ module ActiveSupport
assert_equal "x", number_helper.number_to_human('x')
end
end
-
+
def test_extending_or_including_number_helper_correctly_hides_private_methods
[@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper|
assert !number_helper.respond_to?(:valid_float?)
assert number_helper.respond_to?(:valid_float?, true)
end
end
-
end
end
end
diff --git a/activesupport/test/string_inquirer_test.rb b/activesupport/test/string_inquirer_test.rb
index bb15916e9e..94d5fe197d 100644
--- a/activesupport/test/string_inquirer_test.rb
+++ b/activesupport/test/string_inquirer_test.rb
@@ -1,15 +1,23 @@
require 'abstract_unit'
class StringInquirerTest < ActiveSupport::TestCase
+ def setup
+ @string_inquirer = ActiveSupport::StringInquirer.new('production')
+ end
+
def test_match
- assert ActiveSupport::StringInquirer.new("production").production?
+ assert @string_inquirer.production?
end
def test_miss
- assert !ActiveSupport::StringInquirer.new("production").development?
+ refute @string_inquirer.development?
end
def test_missing_question_mark
- assert_raise(NoMethodError) { ActiveSupport::StringInquirer.new("production").production }
+ assert_raise(NoMethodError) { @string_inquirer.production }
+ end
+
+ def test_respond_to
+ assert_respond_to @string_inquirer, :development?
end
end
diff --git a/activesupport/test/tagged_logging_test.rb b/activesupport/test/tagged_logging_test.rb
index 0751c2469e..43cf1a8e4f 100644
--- a/activesupport/test/tagged_logging_test.rb
+++ b/activesupport/test/tagged_logging_test.rb
@@ -29,6 +29,11 @@ class TaggedLoggingTest < ActiveSupport::TestCase
assert_equal "[BCX] [Jason] [New] Funky time\n", @output.string
end
+ test "does not strip message content" do
+ @logger.info " Hello"
+ assert_equal " Hello\n", @output.string
+ end
+
test "provides access to the logger instance" do
@logger.tagged("BCX") { |logger| logger.info "Funky time" }
assert_equal "[BCX] Funky time\n", @output.string
diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb
index b7076e9e58..b5d8142458 100644
--- a/activesupport/test/transliterate_test.rb
+++ b/activesupport/test/transliterate_test.rb
@@ -1,7 +1,6 @@
# encoding: utf-8
require 'abstract_unit'
require 'active_support/inflector/transliterate'
-require 'active_support/core_ext/object/inclusion'
class TransliterateTest < ActiveSupport::TestCase
@@ -16,7 +15,7 @@ class TransliterateTest < ActiveSupport::TestCase
# create string with range of Unicode"s western characters with
# diacritics, excluding the division and multiplication signs which for
# some reason or other are floating in the middle of all the letters.
- string = (0xC0..0x17E).to_a.reject {|c| c.in?([0xD7, 0xF7])}.pack("U*")
+ string = (0xC0..0x17E).to_a.reject {|c| [0xD7, 0xF7].include?(c)}.pack("U*")
string.each_char do |char|
assert_match %r{^[a-zA-Z']*$}, ActiveSupport::Inflector.transliterate(string)
end
diff --git a/guides/code/getting_started/config/initializers/session_store.rb b/guides/code/getting_started/config/initializers/session_store.rb
index 1a67af58b5..3b2ca93ab9 100644
--- a/guides/code/getting_started/config/initializers/session_store.rb
+++ b/guides/code/getting_started/config/initializers/session_store.rb
@@ -1,8 +1,3 @@
# Be sure to restart your server when you modify this file.
Blog::Application.config.session_store :cookie_store, key: '_blog_session'
-
-# Use the database for sessions instead of the cookie-based default,
-# which shouldn't be used to store highly confidential information
-# (create the session table with "rails generate session_migration")
-# Blog::Application.config.session_store :active_record_store
diff --git a/guides/rails_guides/textile_extensions.rb b/guides/rails_guides/textile_extensions.rb
index 0a002a785f..1faddd4ca0 100644
--- a/guides/rails_guides/textile_extensions.rb
+++ b/guides/rails_guides/textile_extensions.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/object/inclusion'
-
module RedCloth::Formatters::HTML
def emdash(opts)
"--"
diff --git a/guides/source/4_0_release_notes.textile b/guides/source/4_0_release_notes.textile
index d545798f6f..df932603f7 100644
--- a/guides/source/4_0_release_notes.textile
+++ b/guides/source/4_0_release_notes.textile
@@ -82,8 +82,6 @@ will generate the model with <tt>belongs_to :supplier, polymorphic: true</tt> as
* Load all environments available in <tt>config.paths["config/environments"]</tt>.
-* The application generator generates <tt>public/humans.txt</tt> with some basic data.
-
* Add <tt>config.queue_consumer</tt> to allow the default consumer to be configurable.
* Add <tt>Rails.queue</tt> as an interface with a default implementation that consumes jobs in a separate thread.
@@ -196,6 +194,37 @@ h5(#actioncontroller_deprecations). Deprecations
h4. Action Dispatch
+* Add Routing Concerns to declare common routes that can be reused inside others resources and routes.
+
+Code before:
+
+<ruby>
+resources :messages do
+ resources :comments
+end
+
+resources :posts do
+ resources :comments
+ resources :images, only: :index
+end
+</ruby>
+
+Code after:
+
+<ruby>
+concern :commentable do
+ resources :comments
+end
+
+concern :image_attachable do
+ resources :images, only: :index
+end
+
+resources :messages, concerns: :commentable
+
+resources :posts, concerns: [:commentable, :image_attachable]
+</ruby>
+
* Show routes in exception page while debugging a <tt>RoutingError</tt> in development.
* Include <tt>mounted_helpers</tt> (helpers for accessing mounted engines) in <tt>ActionDispatch::IntegrationTest</tt> by default.
@@ -701,6 +730,8 @@ where(...).remove_conditions # => still has conditions
* The migration generator now creates a join table with (commented) indexes every time the migration name contains the word "join_table".
+* <tt>ActiveRecord::SessionStore</tt> is removed from Rails 4.0 and is now a separate "gem":https://github.com/rails/activerecord-session_store.
+
h3. Active Model
* Changed <tt>AM::Serializers::JSON.include_root_in_json</tt> default value to false. Now, AM Serializers and AR objects have the same default behaviour.
@@ -747,6 +778,8 @@ h3. Active Resource
h3. Active Support
+* Add default values to all <tt>ActiveSupport::NumberHelper</tt> methods, to avoid errors with empty locales or missing values.
+
* <tt>Time#change</tt> now works with time values with offsets other than UTC or the local time zone.
* Add <tt>Time#prev_quarter</tt> and <tt>Time#next_quarter</tt> short-hands for <tt>months_ago(3)</tt> and <tt>months_since(3)</tt>.
diff --git a/guides/source/action_controller_overview.textile b/guides/source/action_controller_overview.textile
index cc3350819b..f861b233d2 100644
--- a/guides/source/action_controller_overview.textile
+++ b/guides/source/action_controller_overview.textile
@@ -168,8 +168,8 @@ h3. Session
Your application has a session for each user in which you can store small amounts of data that will be persisted between requests. The session is only available in the controller and the view and can use one of a number of different storage mechanisms:
* ActionDispatch::Session::CookieStore - Stores everything on the client.
-* ActiveRecord::SessionStore - Stores the data in a database using Active Record.
* ActionDispatch::Session::CacheStore - Stores the data in the Rails cache.
+* ActionDispatch::Session::ActiveRecordStore - Stores the data in a database using Active Record. (require `activerecord-session_store` gem).
* ActionDispatch::Session::MemCacheStore - Stores the data in a memcached cluster (this is a legacy implementation; consider using CacheStore instead).
All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure).
@@ -187,7 +187,7 @@ If you need a different session storage mechanism, you can change it in the +con
<ruby>
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
-# (create the session table with "script/rails g session_migration")
+# (create the session table with "script/rails g active_record:session_migration")
# YourApp::Application.config.session_store :active_record_store
</ruby>
@@ -478,9 +478,9 @@ class ChangesController < ActionController::Base
end
</ruby>
-Note that an around filter wraps also rendering. In particular, if in the example above the view itself reads from the database via a scope or whatever, it will do so within the transaction and thus present the data to preview.
+Note that an around filter also wraps rendering. In particular, if in the example above, the view itself reads from the database (e.g. via a scope), it will do so within the transaction and thus present the data to preview.
-They can choose not to yield and build the response themselves, in which case the action is not run.
+You can choose not to yield and build the response yourself, in which case the action will not be run.
h4. Other Ways to Use Filters
diff --git a/guides/source/action_mailer_basics.textile b/guides/source/action_mailer_basics.textile
index 54e55d7260..abfa68b76d 100644
--- a/guides/source/action_mailer_basics.textile
+++ b/guides/source/action_mailer_basics.textile
@@ -4,7 +4,7 @@ This guide should provide you with all you need to get started in sending and re
endprologue.
-WARNING. This Guide is based on Rails 3.2. Some of the code shown here will not work in earlier versions of Rails.
+WARNING. This guide is based on Rails 3.2. Some of the code shown here will not work in earlier versions of Rails.
h3. Introduction
@@ -84,7 +84,7 @@ Create a file called +welcome_email.html.erb+ in +app/views/user_mailer/+. This
</html>
</erb>
-It is also a good idea to make a text part for this email, to do this, create a file called +welcome_email.text.erb+ in +app/views/user_mailer/+:
+It is also a good idea to make a text part for this email. To do this, create a file called +welcome_email.text.erb+ in +app/views/user_mailer/+:
<erb>
Welcome to example.com, <%= @user.name %>
@@ -144,7 +144,7 @@ The method +welcome_email+ returns a <tt>Mail::Message</tt> object which can the
NOTE: In previous versions of Rails, you would call +deliver_welcome_email+ or +create_welcome_email+. This has been deprecated in Rails 3.0 in favour of just calling the method name itself.
-WARNING: Sending out an email should only take a fraction of a second, but if you are planning on sending out many emails, or you have a slow domain resolution service, you might want to investigate using a background process like Delayed Job.
+WARNING: Sending out an email should only take a fraction of a second. If you are planning on sending out many emails, or you have a slow domain resolution service, you might want to investigate using a background process like Delayed Job.
h4. Auto encoding header values
@@ -152,14 +152,14 @@ Action Mailer now handles the auto encoding of multibyte characters inside of he
If you are using UTF-8 as your character set, you do not have to do anything special, just go ahead and send in UTF-8 data to the address fields, subject, keywords, filenames or body of the email and Action Mailer will auto encode it into quoted printable for you in the case of a header field or Base64 encode any body parts that are non US-ASCII.
-For more complex examples such as defining alternate character sets or self encoding text first, please refer to the Mail library.
+For more complex examples such as defining alternate character sets or self-encoding text first, please refer to the Mail library.
h4. Complete List of Action Mailer Methods
There are just three methods that you need to send pretty much any email message:
-* <tt>headers</tt> - Specifies any header on the email you want, you can pass a hash of header field names and value pairs, or you can call <tt>headers[:field_name] = 'value'</tt>
-* <tt>attachments</tt> - Allows you to add attachments to your email, for example <tt>attachments['file-name.jpg'] = File.read('file-name.jpg')</tt>
+* <tt>headers</tt> - Specifies any header on the email you want. You can pass a hash of header field names and value pairs, or you can call <tt>headers[:field_name] = 'value'</tt>.
+* <tt>attachments</tt> - Allows you to add attachments to your email. For example, <tt>attachments['file-name.jpg'] = File.read('file-name.jpg')</tt>.
* <tt>mail</tt> - Sends the actual email itself. You can pass in headers as a hash to the mail method as a parameter, mail will then create an email, either plain text, or multipart, depending on what email templates you have defined.
h5. Custom Headers
@@ -184,7 +184,7 @@ headers["X-Spam"] = value
headers {"X-Spam" => value, "X-Special" => another_value}
</ruby>
-TIP: All <tt>X-Value</tt> headers per the RFC2822 can appear more than one time. If you want to delete an <tt>X-Value</tt> header, you need to assign it a value of <tt>nil</tt>.
+TIP: All <tt>X-Value</tt> headers per the RFC2822 can appear more than once. If you want to delete an <tt>X-Value</tt> header, you need to assign it a value of <tt>nil</tt>.
h5. Adding Attachments
@@ -196,7 +196,7 @@ Adding attachments has been simplified in Action Mailer 3.0.
attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
</ruby>
-NOTE: Mail will automatically Base64 encode an attachment, if you want something different, pre-encode your content and pass in the encoded content and encoding in a +Hash+ to the +attachments+ method.
+NOTE: Mail will automatically Base64 encode an attachment. If you want something different, pre-encode your content and pass in the encoded content and encoding in a +Hash+ to the +attachments+ method.
* Pass the file name and specify headers and content and Action Mailer and Mail will use the settings you pass in.
@@ -229,7 +229,7 @@ end
<%= image_tag attachments['image.jpg'].url %>
</erb>
-* As this is a standard call to +image_tag+ you can pass in an options hash after the attachment url as you could for any other image:
+* As this is a standard call to +image_tag+ you can pass in an options hash after the attachment URL as you could for any other image:
<erb>
<p>Hello there, this is our image</p>
@@ -240,7 +240,7 @@ end
h5. Sending Email To Multiple Recipients
-It is possible to send email to one or more recipients in one email (for e.g. informing all admins of a new signup) by setting the list of emails to the <tt>:to</tt> key. The list of emails can be an array of email addresses or a single string with the addresses separated by commas.
+It is possible to send email to one or more recipients in one email (e.g., informing all admins of a new signup) by setting the list of emails to the <tt>:to</tt> key. The list of emails can be an array of email addresses or a single string with the addresses separated by commas.
<ruby>
class AdminMailer < ActionMailer::Base
@@ -458,6 +458,7 @@ The following configuration options are best made in one of the environment file
|+perform_deliveries+|Determines whether deliveries are actually carried out when the +deliver+ method is invoked on the Mail message. By default they are, but this can be turned off to help functional testing.|
|+deliveries+|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful for unit and functional testing.|
|+async+|Setting this flag will turn on asynchronous message sending, message rendering and delivery will be pushed to <tt>Rails.queue</tt> for processing.|
+|+default_options+|Allows you to set default values for the <tt>mail</tt> method options (<tt>:from</tt>, <tt>:reply_to</tt>, etc.).|
h4. Example Action Mailer Configuration
@@ -472,6 +473,7 @@ config.action_mailer.delivery_method = :sendmail
# }
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
+config.action_mailer.default_options = {from: "no-replay@example.org"}
</ruby>
h4. Action Mailer Configuration for GMail
diff --git a/guides/source/active_model_basics.textile b/guides/source/active_model_basics.textile
index 7cafff2ad8..2c30ddb84c 100644
--- a/guides/source/active_model_basics.textile
+++ b/guides/source/active_model_basics.textile
@@ -1,18 +1,18 @@
h2. Active Model Basics
-This guide should provide you with all you need to get started using model classes. Active Model allow for Action Pack helpers to interact with non-ActiveRecord models. Active Model also helps building custom ORMs for use outside of the Rails framework.
+This guide should provide you with all you need to get started using model classes. Active Model allows for Action Pack helpers to interact with non-ActiveRecord models. Active Model also helps building custom ORMs for use outside of the Rails framework.
endprologue.
-WARNING. This Guide is based on Rails 3.0. Some of the code shown here will not work in earlier versions of Rails.
+WARNING. This guide is based on Rails 3.0. Some of the code shown here will not work in earlier versions of Rails.
h3. Introduction
-Active Model is a library containing various modules used in developing frameworks that need to interact with the Rails Action Pack library. Active Model provides a known set of interfaces for usage in classes. Some of modules are explained below -
+Active Model is a library containing various modules used in developing frameworks that need to interact with the Rails Action Pack library. Active Model provides a known set of interfaces for usage in classes. Some of modules are explained below.
h4. AttributeMethods
-AttributeMethods module can add custom prefixes and suffixes on methods of a class. It is used by defining the prefixes and suffixes, which methods on the object will use them.
+The AttributeMethods module can add custom prefixes and suffixes on methods of a class. It is used by defining the prefixes and suffixes, which methods on the object will use them.
<ruby>
class Person
@@ -92,7 +92,7 @@ person.to_param #=> nil
h4. Dirty
-An object becomes dirty when an object is gone through one or more changes to its attributes and not yet saved. This gives the ability to check whether an object has been changed or not. It also has attribute based accessor methods. Lets consider a Person class with attributes first_name and last_name
+An object becomes dirty when it has gone through one or more changes to its attributes and has not been saved. This gives the ability to check whether an object has been changed or not. It also has attribute based accessor methods. Let's consider a Person class with attributes first_name and last_name
<ruby>
require 'active_model'
@@ -168,7 +168,7 @@ Track what was the previous value of the attribute.
person.first_name_was #=> "First Name"
</ruby>
-Track both previous and current value of the changed attribute. Returns an array if changed else returns nil
+Track both previous and current value of the changed attribute. Returns an array if changed, else returns nil.
<ruby>
#attr_name_change
diff --git a/guides/source/active_record_querying.textile b/guides/source/active_record_querying.textile
index dff829a4c1..80c9260a0d 100644
--- a/guides/source/active_record_querying.textile
+++ b/guides/source/active_record_querying.textile
@@ -53,6 +53,7 @@ To retrieve objects from the database, Active Record provides several finder met
The methods are:
* +bind+
* +create_with+
+* +eager_load+
* +extending+
* +from+
* +group+
@@ -61,9 +62,11 @@ The methods are:
* +joins+
* +limit+
* +lock+
+* +none+
* +offset+
* +order+
* +none+
+* +preload+
* +readonly+
* +references+
* +reorder+
diff --git a/guides/source/active_record_validations_callbacks.textile b/guides/source/active_record_validations_callbacks.textile
index cf7293bd9e..b866337e3f 100644
--- a/guides/source/active_record_validations_callbacks.textile
+++ b/guides/source/active_record_validations_callbacks.textile
@@ -532,6 +532,16 @@ end
Person.new.valid? => ActiveModel::StrictValidationFailed: Name can't be blank
</ruby>
+There is also an ability to pass custom exception to +:strict+ option
+
+<ruby>
+class Person < ActiveRecord::Base
+ validates :token, :presence => true, :uniqueness => true, :strict => TokenGenerationException
+end
+
+Person.new.valid? => TokenGenerationException: Token can't be blank
+</ruby>
+
h3. Conditional Validation
Sometimes it will make sense to validate an object just when a given predicate is satisfied. You can do that by using the +:if+ and +:unless+ options, which can take a symbol, a string, a +Proc+ or an +Array+. You may use the +:if+ option when you want to specify when the validation *should* happen. If you want to specify when the validation *should not* happen, then you may use the +:unless+ option.
diff --git a/guides/source/active_support_core_extensions.textile b/guides/source/active_support_core_extensions.textile
index 66de6fd310..8748817eb8 100644
--- a/guides/source/active_support_core_extensions.textile
+++ b/guides/source/active_support_core_extensions.textile
@@ -1325,6 +1325,41 @@ that amount of leading whitespace.
NOTE: Defined in +active_support/core_ext/string/strip.rb+.
+h4. +indent+
+
+Indents the lines in the receiver:
+
+<ruby>
+<<EOS.indent(2)
+def some_method
+ some_code
+end
+EOS
+# =>
+ def some_method
+ some_code
+ end
+</ruby>
+
+The second argument, +indent_string+, specifies which indent string to use. The default is +nil+, which tells the method to make an educated guess peeking at the first indented line, and fallback to a space if there is none.
+
+<ruby>
+" foo".indent(2) # => " foo"
+"foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
+"foo".indent(2, "\t") # => "\t\tfoo"
+</ruby>
+
+While +indent_string+ is tipically one space or tab, it may be any string.
+
+The third argument, +indent_empty_lines+, is a flag that says whether empty lines should be indented. Default is false.
+
+<ruby>
+"foo\n\nbar".indent(2) # => " foo\n\n bar"
+"foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
+</ruby>
+
+The +indent!+ method performs indentation in-place.
+
h4. Access
h5. +at(position)+
@@ -1456,13 +1491,9 @@ For example, Action Pack uses this method to load the class that provides a cert
<ruby>
# action_controller/metal/session_management.rb
def session_store=(store)
- if store == :active_record_store
- self.session_store = ActiveRecord::SessionStore
- else
- @@session_store = store.is_a?(Symbol) ?
- ActionDispatch::Session.const_get(store.to_s.camelize) :
- store
- end
+ @@session_store = store.is_a?(Symbol) ?
+ ActionDispatch::Session.const_get(store.to_s.camelize) :
+ store
end
</ruby>
diff --git a/guides/source/api_documentation_guidelines.textile b/guides/source/api_documentation_guidelines.textile
index c6aa1f0a2b..3de5b119ee 100644
--- a/guides/source/api_documentation_guidelines.textile
+++ b/guides/source/api_documentation_guidelines.textile
@@ -127,7 +127,7 @@ class Array
end
</ruby>
-WARNING: Using a pair of +&#43;...&#43;+ for fixed-width font only works with *words*; that is: anything matching <tt>\A\w&#43;\z</tt>. For anything else use +&lt;tt&gt;...&lt;/tt&gt;+, notably symbols, setters, inline snippets, etc:
+WARNING: Using a pair of +&#43;...&#43;+ for fixed-width font only works with *words*; that is: anything matching <tt>\A\w&#43;\z</tt>. For anything else use +&lt;tt&gt;...&lt;/tt&gt;+, notably symbols, setters, inline snippets, etc.
h4. Regular Font
diff --git a/guides/source/asset_pipeline.textile b/guides/source/asset_pipeline.textile
index 42b5d102e7..e385ec4f17 100644
--- a/guides/source/asset_pipeline.textile
+++ b/guides/source/asset_pipeline.textile
@@ -432,7 +432,7 @@ NOTE. If you are precompiling your assets locally, you can use +bundle install -
The default matcher for compiling files includes +application.js+, +application.css+ and all non-JS/CSS files (this will include all image assets automatically):
<ruby>
-[ Proc.new{ |path| !File.extname(path).in?(['.js', '.css']) }, /application.(css|js)$/ ]
+[ Proc.new{ |path| !%w(.js .css).include?(File.extname(path)) }, /application.(css|js)$/ ]
</ruby>
NOTE. The matcher (and other members of the precompile array; see below) is applied to final compiled file names. This means that anything that compiles to JS/CSS is excluded, as well as raw JS/CSS files; for example, +.coffee+ and +.scss+ files are *not* automatically included as they compile to JS/CSS.
@@ -443,6 +443,8 @@ If you have other manifests or individual stylesheets and JavaScript files to in
config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js']
</erb>
+NOTE. Always specify an expected compiled filename that ends with js or css, even if you want to add Sass or CoffeeScript files to the precompile array.
+
The rake task also generates a +manifest.yml+ that contains a list with all your assets and their respective fingerprints. This is used by the Rails helper methods to avoid handing the mapping requests back to Sprockets. A typical manifest file looks like:
<plain>
diff --git a/guides/source/association_basics.textile b/guides/source/association_basics.textile
index 2b8b1bd937..d4c0a1ba74 100644
--- a/guides/source/association_basics.textile
+++ b/guides/source/association_basics.textile
@@ -967,10 +967,13 @@ end
h6(#has_one-dependent). +:dependent+
-If you set the +:dependent+ option to +:destroy+, then deleting this object will call the +destroy+ method on the associated object to delete that object. If you set the +:dependent+ option to +:delete+, then deleting this object will delete the associated object _without_ calling its +destroy+ method. If you set the +:dependent+ option to +:nullify+, then deleting this object will set the foreign key in the association object to +NULL+.
-If you set the +:dependent+ option to +:restrict+, then the deletion of the object is restricted if a dependent associated object exist and a +DeleteRestrictionError+ exception is raised.
+Controls what happens to the associated object when its owner is destroyed:
-NOTE: The default behavior for +:dependent => :restrict+ is to raise a +DeleteRestrictionError+ when associated objects exist. Since Rails 4.0 this behavior is being deprecated in favor of adding an error to the base model. To silence the warning in Rails 4.0, you should fix your code to not expect this Exception and add +config.active_record.dependent_restrict_raises = false+ to your application config.
+* +:destroy+ causes the associated object to also be destroyed
+* +:delete+ causes the asssociated object to be deleted directly from the database (so callbacks will not execute)
+* +:nullify+ causes the foreign key to be set to +NULL+. Callbacks are not executed.
+* +:restrict_with_exception+ causes an exception to be raised if there is an associated record
+* +:restrict_with_error+ causes an error to be added to the owner if there is an associated object
h6(#has_one-foreign_key). +:foreign_key+
@@ -1307,10 +1310,13 @@ end
h6(#has_many-dependent). +:dependent+
-If you set the +:dependent+ option to +:destroy+, then deleting this object will call the +destroy+ method on the associated objects to delete those objects. If you set the +:dependent+ option to +:delete_all+, then deleting this object will delete the associated objects _without_ calling their +destroy+ method. If you set the +:dependent+ option to +:nullify+, then deleting this object will set the foreign key in the associated objects to +NULL+.
-If you set the +:dependent+ option to +:restrict+, then the deletion of the object is restricted if a dependent associated object exist and a +DeleteRestrictionError+ exception is raised.
+Controls what happens to the associated objects when their owner is destroyed:
-NOTE: The default behavior for +:dependent => :restrict+ is to raise a +DeleteRestrictionError+ when associated objects exist. Since Rails 4.0 this behavior is being deprecated in favor of adding an error to the base model. To silence the warning in Rails 4.0, you should fix your code to not expect this Exception and add +config.active_record.dependent_restrict_raises = false+ to your application config.
+* +:destroy+ causes all the associated objects to also be destroyed
+* +:delete_all+ causes all the asssociated objects to be deleted directly from the database (so callbacks will not execute)
+* +:nullify+ causes the foreign keys to be set to +NULL+. Callbacks are not executed.
+* +:restrict_with_exception+ causes an exception to be raised if there are any associated records
+* +:restrict_with_error+ causes an error to be added to the owner if there are any associated objects
NOTE: This option is ignored when you use the +:through+ option on the association.
diff --git a/guides/source/caching_with_rails.textile b/guides/source/caching_with_rails.textile
index 3ee36ae971..815b2ef9c2 100644
--- a/guides/source/caching_with_rails.textile
+++ b/guides/source/caching_with_rails.textile
@@ -33,7 +33,7 @@ class ProductsController < ActionController
caches_page :index
def index
- @products = Products.all
+ @products = Product.all
end
end
</ruby>
@@ -52,7 +52,7 @@ class ProductsController < ActionController
caches_page :index
def index
- @products = Products.all
+ @products = Product.all
end
def create
diff --git a/guides/source/configuring.textile b/guides/source/configuring.textile
index cc5c352df4..9db375c2ca 100644
--- a/guides/source/configuring.textile
+++ b/guides/source/configuring.textile
@@ -50,8 +50,6 @@ config.after_initialize do
end
</ruby>
-* +config.allow_concurrency+ should be true to allow concurrent (threadsafe) action processing. False by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Can also be enabled with +threadsafe!+.
-
* +config.asset_host+ sets the host for the assets. Useful when CDNs are used for hosting assets, or when you want to work around the concurrency constraints builtin in browsers using different domain aliases. Shorter version of +config.action_controller.asset_host+.
* +config.asset_path+ lets you decorate asset paths. This can be a callable, a string, or be +nil+ which is the default. For example, the normal path for +blog.js+ would be +/javascripts/blog.js+, let that absolute path be +path+. If +config.asset_path+ is a callable, Rails calls it when generating asset paths passing +path+ as argument. If +config.asset_path+ is a string, it is expected to be a +sprintf+ format string with a +%s+ where +path+ will get inserted. In either case, Rails outputs the decorated path. Shorter version of +config.action_controller.asset_path+.
@@ -89,6 +87,10 @@ end
* +config.dependency_loading+ is a flag that allows you to disable constant autoloading setting it to false. It only has effect if +config.cache_classes+ is true, which it is by default in production mode. This flag is set to false by +config.threadsafe!+.
+* +config.eager_load+ when true, eager loads all registered `config.eager_load_namespaces`. This includes your application, engines, Rails frameworks and any other registered namespace.
+
+* +config.eager_load_namespaces+ registers namespaces that are eager loaded when +config.eager_load+ is true. All namespaces in the list must respond to the +eager_load!+ method.
+
* +config.eager_load_paths+ accepts an array of paths from which Rails will eager load on boot if cache classes is enabled. Defaults to every folder in the +app+ directory of the application.
* +config.encoding+ sets up the application-wide encoding. Defaults to UTF-8.
@@ -109,8 +111,6 @@ end
* +config.middleware+ allows you to configure the application's middleware. This is covered in depth in the "Configuring Middleware":#configuring-middleware section below.
-* +config.preload_frameworks+ enables or disables preloading all frameworks at startup. Enabled by +config.threadsafe!+. Defaults to +nil+, so is disabled.
-
* +config.queue+ configures a different queue implementation for the application. Defaults to +Rails::Queueing::Queue+. Note that, if the default queue is changed, the default +queue_consumer+ is not going to be initialized, it is up to the new queue implementation to handle starting and shutting down its own consumer(s).
* +config.queue_consumer+ configures a different consumer implementation for the default queue. Defaults to +Rails::Queueing::ThreadedConsumer+.
@@ -127,11 +127,7 @@ end
config.session_store :my_custom_store
</ruby>
-This custom store must be defined as +ActionDispatch::Session::MyCustomStore+. In addition to symbols, they can also be objects implementing a certain API, like +ActiveRecord::SessionStore+, in which case no special namespace is required.
-
-* +config.threadsafe!+ enables +allow_concurrency+, +cache_classes+, +dependency_loading+ and +preload_frameworks+ to make the application threadsafe.
-
-WARNING: Threadsafe operation is incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+.
+This custom store must be defined as +ActionDispatch::Session::MyCustomStore+.
* +config.time_zone+ sets the default time zone for the application and enables time zone awareness for Active Record.
@@ -203,7 +199,7 @@ Every Rails application comes with a standard set of middleware which it uses in
* +ActionDispatch::SSL+ forces every request to be under HTTPS protocol. Will be available if +config.force_ssl+ is set to +true+. Options passed to this can be configured by using +config.ssl_options+.
* +ActionDispatch::Static+ is used to serve static assets. Disabled if +config.serve_static_assets+ is +true+.
-* +Rack::Lock+ wraps the app in mutex so it can only be called by a single thread at a time. Only enabled if +config.action_controller.allow_concurrency+ is set to +false+, which it is by default.
+* +Rack::Lock+ wraps the app in mutex so it can only be called by a single thread at a time. Only enabled when +config.cache_classes_+ is +false+.
* +ActiveSupport::Cache::Strategy::LocalCache+ serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread.
* +Rack::Runtime+ sets an +X-Runtime+ header, containing the time (in seconds) taken to execute the request.
* +Rails::Rack::Logger+ notifies the logs that the request has began. After request is complete, flushes all the logs.
@@ -286,8 +282,6 @@ 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.dependent_restrict_raises+ will control the behavior when an object with a <tt>:dependent => :restrict</tt> association is deleted. Setting this to false will prevent +DeleteRestrictionError+ from being raised and instead will add an error on the model object. Defaults to false in the development mode.
-
* +config.active_record.mass_assignment_sanitizer+ will determine the strictness of the mass assignment sanitization within Rails. Defaults to +:strict+. In this mode, mass assigning any non-+attr_accessible+ attribute in a +create+ or +update_attributes+ call will raise an exception. Setting this option to +:logger+ will only print to the log file when an attribute is being assigned and will not raise an exception.
The MySQL adapter adds one additional configuration option:
@@ -328,18 +322,16 @@ The caching code adds two additional settings:
* +ActionController::Base.page_cache_extension+ sets the extension to be used when generating pages for the cache (this is ignored if the incoming request already has an extension). The default is +.html+.
-The Active Record session store can also be configured:
-
-* +ActiveRecord::SessionStore::Session.table_name+ sets the name of the table used to store sessions. Defaults to +sessions+.
-
-* +ActiveRecord::SessionStore::Session.primary_key+ sets the name of the ID column used in the sessions table. Defaults to +session_id+.
-
-* +ActiveRecord::SessionStore::Session.data_column_name+ sets the name of the column which stores marshaled session data. Defaults to +data+.
-
h4. Configuring Action Dispatch
* +config.action_dispatch.session_store+ sets the name of the store for session data. The default is +:cookie_store+; other valid options include +:active_record_store+, +:mem_cache_store+ or the name of your own custom class.
+* +config.action_dispatch.default_headers+ is a hash with HTTP headers that are set by default in each response. By default, this is defined as:
+
+<ruby>
+config.action_dispatch.default_headers = { 'X-Frame-Options' => 'SAMEORIGIN', 'X-XSS-Protection' => '1; mode=block', 'X-Content-Type-Options' => 'nosniff' }
+</ruby>
+
* +config.action_dispatch.tld_length+ sets the TLD (top-level domain) length for the application. Defaults to +1+.
* +ActionDispatch::Callbacks.before+ takes a block of code to run before the request.
@@ -652,8 +644,6 @@ Serves as a placeholder so that +:load_environment_config+ can be defined to run
*+load_active_support+* Requires +active_support/dependencies+ which sets up the basis for Active Support. Optionally requires +active_support/all+ if +config.active_support.bare+ is un-truthful, which is the default.
-*+preload_frameworks+* Loads all autoload dependencies of Rails automatically if +config.preload_frameworks+ is +true+ or "truthful". By default this configuration option is disabled. In Rails, when internal classes are referenced for the first time they are autoloaded. +:preload_frameworks+ loads all of this at once on initialization.
-
*+initialize_logger+* Initializes the logger (an +ActiveSupport::BufferedLogger+ object) for the application and makes it accessible at +Rails.logger+, provided that no initializer inserted before this point has defined +Rails.logger+.
*+initialize_cache+* If +Rails.cache+ isn't set yet, initializes the cache by referencing the value in +config.cache_store+ and stores the outcome as +Rails.cache+. If this object responds to the +middleware+ method, its middleware is inserted before +Rack::Runtime+ in the middleware stack.
@@ -748,13 +738,13 @@ The error occurred while evaluating nil.each
*+build_middleware_stack+* Builds the middleware stack for the application, returning an object which has a +call+ method which takes a Rack environment object for the request.
-*+eager_load!+* If +config.cache_classes+ is true, runs the +config.before_eager_load+ hooks and then calls +eager_load!+ which will load all the Ruby files from +config.eager_load_paths+.
+*+eager_load!+* If +config.eager_load+ is true, runs the +config.before_eager_load+ hooks and then calls +eager_load!+ which will load all +config.eager_load_namespaces+.
*+finisher_hook+* Provides a hook for after the initialization of process of the application is complete, as well as running all the +config.after_initialize+ blocks for the application, railties and engines.
*+set_routes_reloader+* Configures Action Dispatch to reload the routes file using +ActionDispatch::Callbacks.to_prepare+.
-*+disable_dependency_loading+* Disables the automatic dependency loading if the +config.cache_classes+ is set to true and +config.dependency_loading+ is set to false.
+*+disable_dependency_loading+* Disables the automatic dependency loading if the +config.eager_load+ is set to true.
h3. Database pooling
diff --git a/guides/source/contributing_to_ruby_on_rails.textile b/guides/source/contributing_to_ruby_on_rails.textile
index dd43ef795f..4bb4e3b546 100644
--- a/guides/source/contributing_to_ruby_on_rails.textile
+++ b/guides/source/contributing_to_ruby_on_rails.textile
@@ -36,20 +36,26 @@ Please don't put "feature request" items into GitHub Issues. If there's a new fe
If you'd like feedback on an idea for a feature before doing the work for make a patch, please send an email to the "rails-core mailing list":https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core. You might get no response, which means that everyone is indifferent. You might find someone who's also interested in building that feature. You might get a "This won't be accepted." But it's the proper place to discuss new ideas. GitHub Issues are not a particularly good venue for the sometimes long and involved discussions new features require.
-h3. Running the Test Suite
+h3. Setting Up a Development Environment
To move on from submitting bugs to helping resolve existing issues or contributing your own code to Ruby on Rails, you _must_ be able to run its test suite. In this section of the guide you'll learn how to set up the tests on your own computer.
-h4. Install Git
+h4. The Easy Way
-Ruby on Rails uses git for source code control. The "git homepage":http://git-scm.com/ has installation instructions. There are a variety of resources on the net that will help you get familiar with git:
+The easiest way to get a development environment ready to hack is to use the "Rails development box":https://github.com/rails/rails-dev-box.
-* "Everyday Git":http://schacon.github.com/git/everyday.html will teach you just enough about git to get by.
-* The "PeepCode screencast":https://peepcode.com/products/git on git ($9) is easier to follow.
-* "GitHub":http://help.github.com offers links to a variety of git resources.
-* "Pro Git":http://progit.org/book/ is an entire book about git with a Creative Commons license.
+h4. The Hard Way
-h4. Clone the Ruby on Rails Repository
+h5. Install Git
+
+Ruby on Rails uses Git for source code control. The "Git homepage":http://git-scm.com/ has installation instructions. There are a variety of resources on the net that will help you get familiar with Git:
+
+* "Everyday Git":http://schacon.github.com/git/everyday.html will teach you just enough about Git to get by.
+* The "PeepCode screencast":https://peepcode.com/products/git on Git ($9) is easier to follow.
+* "GitHub":http://help.github.com offers links to a variety of Git resources.
+* "Pro Git":http://git-scm.com/book is an entire book about Git with a Creative Commons license.
+
+h5. Clone the Ruby on Rails Repository
Navigate to the folder where you want the Ruby on Rails source code (it will create its own +rails+ subdirectory) and run:
@@ -58,7 +64,7 @@ $ git clone git://github.com/rails/rails.git
$ cd rails
</shell>
-h4. Set up and Run the Tests
+h5. Set up and Run the Tests
The test suite must pass with any submitted code. No matter whether you are writing a new patch, or evaluating someone else's, you need to be able to run the tests.
@@ -74,7 +80,7 @@ If you are on Fedora or CentOS, you can run
$ sudo yum install libxml2 libxml2-devel libxslt libxslt-devel
</shell>
-If you have any problems with these libraries, you should install them manually compiling the source code. Just follow the instructions "here":http://nokogiri.org/tutorials/installing_nokogiri.html#red_hat__centos .
+If you have any problems with these libraries, you should install them manually compiling the source code. Just follow the instructions at the "Red Hat/CentOS section of the Nokogiri tutorials":http://nokogiri.org/tutorials/installing_nokogiri.html#red_hat__centos .
Also, SQLite3 and its development files for the +sqlite3-ruby+ gem -- in Ubuntu you're done with just
@@ -128,36 +134,17 @@ $ cd actionpack
$ bundle exec ruby -Itest test/template/form_helper_test.rb
</shell>
-h4. Warnings
-
-The test suite runs with warnings enabled. Ideally, Ruby on Rails should issue no warnings, but there may be a few, as well as some from third-party libraries. Please ignore (or fix!) them, if any, and submit patches that do not issue new warnings.
-
-As of this writing (December, 2010) they are specially noisy with Ruby 1.9. If you are sure about what you are doing and would like to have a more clear output, there's a way to override the flag:
-
-<shell>
-$ RUBYOPT=-W0 bundle exec rake test
-</shell>
-
-h4. Testing Active Record
+h5. Active Record Setup
The test suite of Active Record attempts to run four times: once for SQLite3, once for each of the two MySQL gems (+mysql+ and +mysql2+), and once for PostgreSQL. We are going to see now how to set up the environment for them.
WARNING: If you're working with Active Record code, you _must_ ensure that the tests pass for at least MySQL, PostgreSQL, and SQLite3. Subtle differences between the various adapters have been behind the rejection of many patches that looked OK when tested only against MySQL.
-h5. Database Configuration
+h6. Database Configuration
The Active Record test suite requires a custom config file: +activerecord/test/config.yml+. An example is provided in +activerecord/test/config.example.yml+ which can be copied and used as needed for your environment.
-h5. SQLite3
-
-The gem +sqlite3-ruby+ does not belong to the "db" group. Indeed, if you followed the instructions above you're ready. This is how you run the Active Record test suite only for SQLite3:
-
-<shell>
-$ cd activerecord
-$ bundle exec rake test_sqlite3
-</shell>
-
-h5. MySQL and PostgreSQL
+h6. MySQL and PostgreSQL
To be able to run the suite for MySQL and PostgreSQL we need their gems. Install first the servers, their client libraries, and their development files. In Ubuntu just run
@@ -217,6 +204,15 @@ NOTE: You'll see the following warning (or localized warning) during activating
If you’re using another database, check the file +activerecord/test/config.yml+ or +activerecord/test/config.example.yml+ for default connection information. You can edit +activerecord/test/config.yml+ to provide different credentials on your machine if you must, but obviously you should not push any such changes back to Rails.
+h3. Testing Active Record
+
+This is how you run the Active Record test suite only for SQLite3:
+
+<shell>
+$ cd activerecord
+$ bundle exec rake test_sqlite3
+</shell>
+
You can now run the tests as you did for +sqlite3+. The tasks are respectively
<shell>
@@ -225,7 +221,7 @@ test_mysql2
test_postgresql
</shell>
-As we mentioned before
+Finally,
<shell>
$ bundle exec rake test
@@ -241,6 +237,16 @@ $ ARCONN=sqlite3 ruby -Itest test/cases/associations/has_many_associations_test.
You can invoke +test_jdbcmysql+, +test_jdbcsqlite3+ or +test_jdbcpostgresql+ also. See the file +activerecord/RUNNING_UNIT_TESTS+ for information on running more targeted database tests, or the file +ci/travis.rb+ for the test suite run by the continuous integration server.
+h4. Warnings
+
+The test suite runs with warnings enabled. Ideally, Ruby on Rails should issue no warnings, but there may be a few, as well as some from third-party libraries. Please ignore (or fix!) them, if any, and submit patches that do not issue new warnings.
+
+As of this writing (December, 2010) they are specially noisy with Ruby 1.9. If you are sure about what you are doing and would like to have a more clear output, there's a way to override the flag:
+
+<shell>
+$ RUBYOPT=-W0 bundle exec rake test
+</shell>
+
h4. Older Versions of Ruby on Rails
If you want to add a fix to older versions of Ruby on Rails, you'll need to set up and switch to your own local tracking branch. Here is an example to switch to the 3-0-stable branch:
@@ -250,7 +256,7 @@ $ git branch --track 3-0-stable origin/3-0-stable
$ git checkout 3-0-stable
</shell>
-TIP: You may want to "put your git branch name in your shell prompt":http://qugstart.com/blog/git-and-svn/add-colored-git-branch-name-to-your-shell-prompt/ to make it easier to remember which version of the code you're working with.
+TIP: You may want to "put your Git branch name in your shell prompt":http://qugstart.com/blog/git-and-svn/add-colored-git-branch-name-to-your-shell-prompt/ to make it easier to remember which version of the code you're working with.
h3. Helping to Resolve Existing Issues
@@ -331,7 +337,7 @@ $ cd rails
$ git checkout -b my_new_branch
</shell>
-It doesn’t matter much what name you use, because this branch will only exist on your local computer and your personal repository on Github. It won't be part of the Rails git repository.
+It doesn’t matter much what name you use, because this branch will only exist on your local computer and your personal repository on Github. It won't be part of the Rails Git repository.
h4. Write Your Code
@@ -359,6 +365,31 @@ Rails follows a simple set of coding style conventions.
The above are guidelines -- please use your best judgment in using them.
+h4. Updating the CHANGELOG
+
+The CHANGELOG is an important part of every release. It keeps the list of changes for every Rails version.
+
+You should add an entry to the CHANGELOG of the framework that you modified if you're adding or removing a feature, commiting a bug fix or adding deprecation notices. Refactorings and documentation changes generally should not go to the CHANGELOG.
+
+A CHANGELOG entry should summarize what was changed and should end with author's name. You can use multiple lines if you need more space and you can attach code examples indented with 4 spaces. If a change is related to a specific issue, you should attach issue's number. Here is an example CHANGELOG entry:
+
+<plain>
+* Summary of a change that briefly describes what was changed. You can use multiple
+ lines and wrap them at around 80 characters. Code examples are ok, too, if needed:
+
+ class Foo
+ def bar
+ puts 'baz'
+ end
+ end
+
+ You can continue after the code example and you can attach issue number. GH#1234
+
+ * Your Name *
+</plain>
+
+Your name can be added directly after the last word if you don't provide any code examples or don't need multiple paragraphs. Otherwise, it's best to make as a new paragraph.
+
h4. Sanity Check
You should not be the only person who looks at the code before you submit it. You know at least one other Rails developer, right? Show them what you’re doing and ask for feedback. Doing this in private before you push a patch out publicly is the “smoke test” for a patch: if you can’t convince one other developer of the beauty of your code, you’re unlikely to convince the core team either.
@@ -367,7 +398,7 @@ You might want also to check out the "RailsBridge BugMash":http://wiki.railsbrid
h4. Commit Your Changes
-When you're happy with the code on your computer, you need to commit the changes to git:
+When you're happy with the code on your computer, you need to commit the changes to Git:
<shell>
$ git commit -a
@@ -386,7 +417,7 @@ the commit content is obvious, it may not be obvious to others. You
should add such description also if it's already present in bug tracker,
it should not be necessary to visit a webpage to check the history.
-Description can have multiple paragraps and you can use code examples
+Description can have multiple paragraphs and you can use code examples
inside, just indent it with 4 spaces:
class PostsController
@@ -495,7 +526,7 @@ It’s entirely possible that the feedback you get will suggest changes. Don’t
h4. Backporting
-Changes that are merged into master are intended for the next major release of Rails. Sometimes, it might be beneficial for your changes to propagate back to the maintenance releases for older stable branches. Generally, security fixes and bug fixes are good candidates for a backport, while new features and patches that introduce a change in behavior will not be accepted. When in doubt, it is best to consult a rails team member before backporting your changes to avoid wasted effort.
+Changes that are merged into master are intended for the next major release of Rails. Sometimes, it might be beneficial for your changes to propagate back to the maintenance releases for older stable branches. Generally, security fixes and bug fixes are good candidates for a backport, while new features and patches that introduce a change in behavior will not be accepted. When in doubt, it is best to consult a Rails team member before backporting your changes to avoid wasted effort.
For simple fixes, the easiest way to backport your changes is to "extract a diff from your changes in master and apply them to the target branch":http://ariejan.net/2009/10/26/how-to-create-and-apply-a-patch-with-git.
@@ -520,9 +551,9 @@ $ git apply ~/my_changes.patch
This works well for simple changes. However, if your changes are complicated or if the code in master has deviated significantly from your target branch, it might require more work on your part. The difficulty of a backport varies greatly from case to case, and sometimes it is simply not worth the effort.
-Once you have resolved all conflicts and made sure all the tests are passing, push your changes and open a separate pull request for your backport. It is also worth noting that older branches might have a different set of build targets than master. When possible, it is best to first test your backport locally against the ruby versions listed in +.travis.yml+ before submitting your pull request.
+Once you have resolved all conflicts and made sure all the tests are passing, push your changes and open a separate pull request for your backport. It is also worth noting that older branches might have a different set of build targets than master. When possible, it is best to first test your backport locally against the Ruby versions listed in +.travis.yml+ before submitting your pull request.
-And then ... think about your next contribution!
+And then... think about your next contribution!
h3. Rails Contributors
diff --git a/guides/source/engines.textile b/guides/source/engines.textile
index 11c837be32..de4bbb5656 100644
--- a/guides/source/engines.textile
+++ b/guides/source/engines.textile
@@ -655,15 +655,17 @@ This tells the application that you still want to perform a +GET+ request to the
h3. Improving engine functionality
-This section looks at overriding or adding functionality to the views, controllers and models provided by an engine.
+This section explains how to add and/or override engine MVC functionality in the main Rails application.
-h4. Overriding Models
+h4. Overriding Models and Controllers
-Engine Models can be extended by (1) implementing decorators, or (2) including modules.
+Engine model and controller classes can be extended by open classing them in the main Rails application (since model and controller classes are just Ruby classes that inherit Rails specific functionality). Open classing an Engine class redefines it for use in the main applicaiton. This is usually implemented by using the decorator pattern.
-h5. Decorators
+For simple class modifications use Class#class_eval, and for complex class modifications, consider using ActiveSupport::Concern.
-Decorators extends Engine's model classes in the main application by open classing Engine models at run time execution.
+h5. Implementing Decorator Pattern Using Class#class_eval
+
+**Adding** Post#time_since_created,
<ruby>
# MyApp/app/decorators/models/blorgh/post_decorator.rb
@@ -675,15 +677,48 @@ Blorgh::Post.class_eval do
end
</ruby>
-h5. Modules
+<ruby>
+# Blorgh/app/models/post.rb
+
+class Post < ActiveRecord::Base
+ :has_many :comments
+end
+</ruby>
+
+
+**Overriding** Post#summary
-The other strategy is to create modules within the Engine holding all the models' code and include these in the respective Engine's model classes. Thus the Engine's model classes contain a mere include line referencing the respective module.
+<ruby>
+# MyApp/app/decorators/models/blorgh/post_decorator.rb
-Engine models can be overriden in the main application by creating a file with the Engine's same namespace and including the module originally referenced in the Engine's model class. ["**ActiveSupport::Concern**":http://edgeapi.rubyonrails.org/classes/ActiveSupport/Concern.html] helps manage the correct ordering of module dependencies at run time (it's worth it to reach the linked documentation).
+Blorgh::Post.class_eval do
+ def summary
+ "#{title} - #{truncate(text)}"
+ end
+end
+</ruby>
+
+<ruby>
+# Blorgh/app/models/post.rb
+
+class Post < ActiveRecord::Base
+ :has_many :comments
+ def summary
+ "#{title}"
+ end
+end
+</ruby>
+
+
+h5. Implementing Decorator Pattern Using ActiveSupport::Concern
+
+Using Class#class_eval is great for simple adjustments, but for more complex class modifications, you might want to consider using ActiveSupport::Concern. ["**ActiveSupport::Concern**":http://edgeapi.rubyonrails.org/classes/ActiveSupport/Concern.html] helps manage load order of interlinked dependencies at run time allowing you to significantly modularize your code.
+
+**Adding** Post#time_since_created<br/>
+**Overriding** Post#summary
<ruby>
# MyApp/app/models/blorgh/post.rb
-# overrides Blorgh's original Post model
class Blorgh::Post < ActiveRecord::Base
include Blorgh::Concerns::Models::Post
@@ -691,24 +726,30 @@ class Blorgh::Post < ActiveRecord::Base
def time_since_created
Time.current - created_at
end
-end
+ def summary
+ "#{title} - #{truncate(text)}"
+ end
+end
+</ruby>
+<ruby>
# Blorgh/app/models/post.rb
-# this class is overriden by the MyApp Post model
class Post < ActiveRecord::Base
include Blorgh::Concerns::Models::Post
end
+</ruby>
+<ruby>
+# Blorgh/lib/concerns/models/post
-# Blorgh/app/concerns/models/post
-
-module Blorg::Concerns::Models::Post
+module Blorgh::Concerns::Models::Post
extend ActiveSupport::Concern
- # 'included do' causes the code within to be evaluated in the conext
- # where it is included, rather be executed in the module's context.
+ # 'included do' causes the included code to be evaluated in the
+ # conext where it is included (post.rb), rather than be
+ # executed in the module's context (blorgh/concerns/models/post).
included do
attr_accessor :author_name
belongs_to :author, :class_name => "User"
@@ -722,9 +763,8 @@ module Blorg::Concerns::Models::Post
end
end
- # methods defined here will be instance methods by default
- def some_method
- 'some method string'
+ def summary
+ "#{title}"
end
module ClassMethods
diff --git a/guides/source/i18n.textile b/guides/source/i18n.textile
index c782539399..67863e590c 100644
--- a/guides/source/i18n.textile
+++ b/guides/source/i18n.textile
@@ -565,7 +565,7 @@ I18n.translate :thanks, :name => 'Jeremy'
# => 'Thanks Jeremy!'
</ruby>
-If a translation uses +:default+ or +:scope+ as an interpolation variable, an I+18n::ReservedInterpolationKey+ exception is raised. If a translation expects an interpolation variable, but this has not been passed to +#translate+, an +I18n::MissingInterpolationArgument+ exception is raised.
+If a translation uses +:default+ or +:scope+ as an interpolation variable, an +I18n::ReservedInterpolationKey+ exception is raised. If a translation expects an interpolation variable, but this has not been passed to +#translate+, an +I18n::MissingInterpolationArgument+ exception is raised.
h4. Pluralization
diff --git a/guides/source/initialization.textile b/guides/source/initialization.textile
index 43d89eac4b..b23f31cb1a 100644
--- a/guides/source/initialization.textile
+++ b/guides/source/initialization.textile
@@ -17,7 +17,7 @@ NOTE: Paths in this guide are relative to Rails or a Rails application unless ot
TIP: If you want to follow along while browsing the Rails "source
code":https://github.com/rails/rails, we recommend that you use the +t+
-key binding to open the file finder inside github and find files
+key binding to open the file finder inside GitHub and find files
quickly.
h3. Launch!
@@ -359,7 +359,7 @@ set earlier) is required.
h4. +config/application+
When +require APP_PATH+ is executed, +config/application.rb+ is loaded.
-This is a file exists in your app and it's free for you to change based
+This file exists in your app and it's free for you to change based
on your needs.
h4. +Rails::Server#start+
@@ -453,7 +453,7 @@ end
The interesting part for a Rails app is the last line, +server.run+. Here we encounter the +wrapped_app+ method again, which this time
we're going to explore more (even though it was executed before, and
-thus memoized by now).
+thus memorized by now).
<ruby>
@wrapped_app ||= build_app app
@@ -540,12 +540,12 @@ end
</ruby>
This is where all the Rails frameworks are loaded and thus made
-available to the application. We wont go into detail of what happens
+available to the application. We won't go into detail of what happens
inside each of those frameworks, but you're encouraged to try and
explore them on your own.
For now, just keep in mind that common functionality like Rails engines,
-I18n and Rails configuration is all bein defined here.
+I18n and Rails configuration is all being defined here.
h4. Back to +config/environment.rb+
@@ -657,10 +657,10 @@ def self.run(app, options={})
end
</ruby>
-We wont dig into the server configuration itself, but this is
+We won't dig into the server configuration itself, but this is
the last piece of our journey in the Rails initialization process.
-This high level overview will help you understand when you code is
+This high level overview will help you understand when your code is
executed and how, and overall become a better Rails developer. If you
still want to know more, the Rails source code itself is probably the
best place to go next.
diff --git a/guides/source/kindle/KINDLE.md b/guides/source/kindle/KINDLE.md
index a7d9a4e4cf..08937e053e 100644
--- a/guides/source/kindle/KINDLE.md
+++ b/guides/source/kindle/KINDLE.md
@@ -9,16 +9,16 @@
## Resources
- * [StackOverflow: Kindle Periodical Format](http://stackoverflow.com/questions/5379565/kindle-periodical-format)
+ * [Stack Overflow: Kindle Periodical Format](http://stackoverflow.com/questions/5379565/kindle-periodical-format)
* Example Periodical [.ncx](https://gist.github.com/808c971ed087b839d462) and [.opf](https://gist.github.com/d6349aa8488eca2ee6d0)
- * [Kindle Publishing guidelines](http://kindlegen.s3.amazonaws.com/AmazonKindlePublishingGuidelines.pdf)
+ * [Kindle Publishing Guidelines](http://kindlegen.s3.amazonaws.com/AmazonKindlePublishingGuidelines.pdf)
* [KindleGen & Kindle Previewer](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000234621)
## TODO
### Post release
- * Integrate generated Kindle document in to published HTML guides
+ * Integrate generated Kindle document into published HTML guides
* Tweak heading styles (most docs use h3/h4/h5, which end up being smaller than the text under it)
* Tweak table styles (smaller text? Many of the tables are unusable on a Kindle in portrait mode)
* Have the HTML/XML TOC 'drill down' into the TOCs of the individual guides
diff --git a/guides/source/kindle/welcome.html.erb b/guides/source/kindle/welcome.html.erb
index e30704c4e6..610a71570f 100644
--- a/guides/source/kindle/welcome.html.erb
+++ b/guides/source/kindle/welcome.html.erb
@@ -2,4 +2,4 @@
<h3>Kindle Edition</h3>
-The Kindle Edition of the Rails Guides should be considered a work in progress. Feedback is really welcome, please see the "Feedback" section at the end of each guide for instructions.
+The Kindle Edition of the Rails Guides should be considered a work in progress. Feedback is really welcome. Please see the "Feedback" section at the end of each guide for instructions.
diff --git a/guides/source/migrations.textile b/guides/source/migrations.textile
index 06e85e5914..a7de21b754 100644
--- a/guides/source/migrations.textile
+++ b/guides/source/migrations.textile
@@ -14,7 +14,7 @@ Migrations also allow you to describe these transformations using Ruby. The
great thing about this is that (like most of Active Record's functionality) it
is database independent: you don't need to worry about the precise syntax of
+CREATE TABLE+ any more than you worry about variations on +SELECT *+ (you can
-drop down to raw SQL for database specific features). For example you could use
+drop down to raw SQL for database specific features). For example, you could use
SQLite3 in development, but MySQL in production.
In this guide, you'll learn all about migrations including:
@@ -123,10 +123,10 @@ database independent way (you'll read about them in detail later):
* +rename_column+
* +remove_reference+
-If you need to perform tasks specific to your database (for example create a
+If you need to perform tasks specific to your database (e.g., create a
"foreign key":#active-record-and-referential-integrity constraint) then the
+execute+ method allows you to execute arbitrary SQL. A migration is just a
-regular Ruby class so you're not limited to these functions. For example after
+regular Ruby class so you're not limited to these functions. For example, after
adding a column you could write code to set the value of that column for
existing records (if necessary using your models).
@@ -165,7 +165,7 @@ config.active_record.timestamped_migrations = false
The combination of timestamps and recording which migrations have been run
allows Rails to handle common situations that occur with multiple developers.
-For example Alice adds migrations +20080906120000+ and +20080906123000+ and Bob
+For example, Alice adds migrations +20080906120000+ and +20080906123000+ and Bob
adds +20080906124500+ and runs it. Alice finishes her changes and checks in her
migrations and Bob pulls down the latest changes. When Bob runs +rake db:migrate+,
Rails knows that it has not run Alice's two migrations so it executes the +up+ method for each migration.
@@ -182,7 +182,7 @@ migration again: Rails thinks it has already run the migration and so will do
nothing when you run +rake db:migrate+. You must rollback the migration (for
example with +rake db:rollback+), edit your migration and then run +rake db:migrate+ to run the corrected version.
-In general editing existing migrations is not a good idea: you will be creating
+In general, editing existing migrations is not a good idea. You will be creating
extra work for yourself and your co-workers and cause major headaches if the
existing version of the migration has already been run on production machines.
Instead, you should write a new migration that performs the changes you require.
@@ -209,8 +209,7 @@ Active Record supports the following database column types:
These will be mapped onto an appropriate underlying database type. For example,
with MySQL the type +:string+ is mapped to +VARCHAR(255)+. You can create
-columns of types not supported by Active Record when using the non-sexy syntax,
-for example
+columns of types not supported by Active Record when using the non-sexy syntax such as
<ruby>
create_table :products do |t|
@@ -255,7 +254,7 @@ by Active Record).
h4. Creating a Standalone Migration
-If you are creating migrations for other purposes (for example to add a column
+If you are creating migrations for other purposes (e.g., to add a column
to an existing table) then you can also use the migration generator:
<shell>
@@ -309,7 +308,7 @@ class RemovePartNumberFromProducts < ActiveRecord::Migration
end
</ruby>
-You are not limited to one magically generated column, for example
+You are not limited to one magically generated column. For example
<shell>
$ rails generate migration AddDetailsToProducts part_number:string price:decimal
@@ -334,7 +333,7 @@ NOTE: The generated migration file for destructive migrations will still be
old-style using the +up+ and +down+ methods. This is because Rails needs to know
the original data types defined when you made the original changes.
-Also the generator accepts column type as +references+(also available as +belongs_to+), for instance
+Also, the generator accepts column type as +references+(also available as +belongs_to+). For instance
<shell>
$ rails generate migration AddUserRefToProducts user:references
@@ -362,7 +361,7 @@ following modifiers:
* +scale+ Defines the scale for the +decimal+ fields
* +polymorphic+ Adds a +type+ column for +belongs_to+ associations
-For instance running
+For instance, running
<shell>
$ rails generate migration AddDetailsToProducts price:decimal{5,2} supplier:references{polymorphic}
@@ -541,8 +540,8 @@ support":#active-record-and-referential-integrity.
If the helpers provided by Active Record aren't enough you can use the +execute+
method to execute arbitrary SQL.
-For more details and examples of individual methods, check the API documentation,
-in particular the documentation for
+For more details and examples of individual methods, check the API documentation.
+In particular the documentation for
"<tt>ActiveRecord::ConnectionAdapters::SchemaStatements</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html
(which provides the methods available in the +up+ and +down+ methods),
"<tt>ActiveRecord::ConnectionAdapters::TableDefinition</tt>":http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html
@@ -644,7 +643,7 @@ down to, but not including, 20080906120000.
h4. Rolling Back
-A common task is to rollback the last migration, for example if you made a
+A common task is to rollback the last migration. For example, if you made a
mistake in it and wish to correct it. Rather than tracking down the version
number associated with the previous migration you can run
@@ -839,7 +838,7 @@ An error has occurred, this and all later migrations canceled:
undefined method `fuzz' for #<Product:0x000001049b14a0>
</plain>
-A fix for this is to create a local model within the migration. This keeps rails
+A fix for this is to create a local model within the migration. This keeps Rails
from running the validations, so that the migrations run to completion.
When using a faux model, it's a good idea to call
diff --git a/guides/source/performance_testing.textile b/guides/source/performance_testing.textile
index b7d4f1ef33..6c5676c346 100644
--- a/guides/source/performance_testing.textile
+++ b/guides/source/performance_testing.textile
@@ -649,6 +649,7 @@ h4. Rails Plugins and Gems
* "Palmist":http://www.flyingmachinestudios.com/programming/announcing-palmist
* "Rails Footnotes":https://github.com/josevalim/rails-footnotes/tree/master
* "Query Reviewer":https://github.com/dsboulder/query_reviewer/tree/master
+* "MiniProfiler":http://www.miniprofiler.com
h4. Generic Tools
@@ -668,4 +669,4 @@ Rails has been lucky to have a few companies dedicated to Rails-specific
performance tools. A couple of those are:
* "New Relic":http://www.newrelic.com
-* "Scout":http://scoutapp.com \ No newline at end of file
+* "Scout":http://scoutapp.com
diff --git a/guides/source/plugins.textile b/guides/source/plugins.textile
index 9001857a5f..fbd317f0c2 100644
--- a/guides/source/plugins.textile
+++ b/guides/source/plugins.textile
@@ -13,7 +13,7 @@ After reading this guide you should be familiar with:
This guide describes how to build a test-driven plugin that will:
-* Extend core ruby classes like Hash and String
+* Extend core Ruby classes like Hash and String
* Add methods to ActiveRecord::Base in the tradition of the 'acts_as' plugins
* Give you information about where to put generators in your plugin.
@@ -28,7 +28,7 @@ h3. Setup
_"vendored plugins"_ were available in previous versions of Rails, but they are deprecated in
Rails 3.2, and will not be available in the future.
-Currently, Rails plugins are built as gems, _gemified plugins_. They can be shared accross
+Currently, Rails plugins are built as gems, _gemified plugins_. They can be shared across
different rails applications using RubyGems and Bundler if desired.
h4. Generate a gemified plugin.
@@ -392,7 +392,7 @@ the creation of generators can be found in the "Generators Guide":generators.htm
h3. Publishing your Gem
Gem plugins currently in development can easily be shared from any Git repository. To share the Yaffle gem with others, simply
-commit the code to a Git repository (like Github) and add a line to the Gemfile of the application in question:
+commit the code to a Git repository (like GitHub) and add a line to the Gemfile of the application in question:
<ruby>
gem 'yaffle', :git => 'git://github.com/yaffle_watcher/yaffle.git'
@@ -401,7 +401,7 @@ gem 'yaffle', :git => 'git://github.com/yaffle_watcher/yaffle.git'
After running +bundle install+, your gem functionality will be available to the application.
When the gem is ready to be shared as a formal release, it can be published to "RubyGems":http://www.rubygems.org.
-For more information about publishing gems to RubyGems, see: "http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html":http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html
+For more information about publishing gems to RubyGems, see: "Creating and Publishing Your First Ruby Gem":http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html
h3. RDoc Documentation
@@ -414,7 +414,7 @@ The first step is to update the README file with detailed information about how
* How to add the functionality to the app (several examples of common use cases)
* Warnings, gotchas or tips that might help users and save them time
-Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add '#:nodoc:' comments to those parts of the code that are not included in the public api.
+Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add '#:nodoc:' comments to those parts of the code that are not included in the public API.
Once your comments are good to go, navigate to your plugin directory and run:
@@ -425,6 +425,6 @@ $ rake rdoc
h4. References
* "Developing a RubyGem using Bundler":https://github.com/radar/guides/blob/master/gem-development.md
-* "Using Gemspecs As Intended":http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/
+* "Using .gemspecs as Intended":http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/
* "Gemspec Reference":http://docs.rubygems.org/read/chapter/20
-* "GemPlugins":http://www.intridea.com/blog/2008/6/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins
+* "GemPlugins: A Brief Introduction to the Future of Rails Plugins":http://www.intridea.com/blog/2008/6/11/gemplugins-a-brief-introduction-to-the-future-of-rails-plugins
diff --git a/guides/source/routing.textile b/guides/source/routing.textile
index cffbf9bec4..bed7d03e06 100644
--- a/guides/source/routing.textile
+++ b/guides/source/routing.textile
@@ -273,6 +273,36 @@ The corresponding route helper would be +publisher_magazine_photo_url+, requirin
TIP: _Resources should never be nested more than 1 level deep._
+h4. Routing concerns
+
+Routing Concerns allows you to declare common routes that can be reused inside others resources and routes.
+
+<ruby>
+concern :commentable do
+ resources :comments
+end
+
+concern :image_attachable do
+ resources :images, only: :index
+end
+</ruby>
+
+These concerns can be used in resources to avoid code duplication and share behavior across routes.
+
+<ruby>
+resources :messages, concerns: :commentable
+
+resources :posts, concerns: [:commentable, :image_attachable]
+</ruby>
+
+Also you can use them in any place that you want inside the routes, for example in a scope or namespace call:
+
+<ruby>
+namespace :posts do
+ concerns :commentable
+end
+</ruby>
+
h4. Creating Paths and URLs From Objects
In addition to using the routing helpers, Rails can also create paths and URLs from an array of parameters. For example, suppose you have this set of routes:
diff --git a/guides/source/security.textile b/guides/source/security.textile
index 49e5da6bb7..773a47ab28 100644
--- a/guides/source/security.textile
+++ b/guides/source/security.textile
@@ -81,9 +81,7 @@ This will also be a good idea, if you modify the structure of an object and old
h4. Session Storage
-NOTE: _Rails provides several storage mechanisms for the session hashes. The most important are +ActiveRecord::SessionStore+ and +ActionDispatch::Session::CookieStore+._
-
-There are a number of session storages, i.e. where Rails saves the session hash and session id. Most real-live applications choose ActiveRecord::SessionStore (or one of its derivatives) over file storage due to performance and maintenance reasons. ActiveRecord::SessionStore keeps the session id and hash in a database table and saves and retrieves the hash on every request.
+NOTE: _Rails provides several storage mechanisms for the session hashes. The most important is +ActionDispatch::Session::CookieStore+._
Rails 2 introduced a new default session storage, CookieStore. CookieStore saves the session hash directly in a cookie on the client-side. The server retrieves the session hash from the cookie and eliminates the need for a session id. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications of it:
diff --git a/guides/source/upgrading_ruby_on_rails.textile b/guides/source/upgrading_ruby_on_rails.textile
index 5024bc4c37..cdf7306264 100644
--- a/guides/source/upgrading_ruby_on_rails.textile
+++ b/guides/source/upgrading_ruby_on_rails.textile
@@ -44,6 +44,8 @@ The <tt>delete</tt> method in collection associations can now receive <tt>Fixnum
Rails 4.0 has changed how orders get stacked in +ActiveRecord::Relation+. In previous versions of rails new order was applied after previous defined order. But this is no long true. Check "ActiveRecord Query guide":active_record_querying.html#ordering for more information.
+Rails 4.0 has changed <tt>serialized_attributes</tt> and <tt>_attr_readonly</tt> to class methods only. Now you shouldn't use instance methods, it's deprecated. You must change them, e.g. <tt>self.serialized_attributes</tt> to <tt>self.class.serialized_attributes</tt>.
+
h4(#active_model4_0). Active Model
Rails 4.0 has changed how errors attach with the <tt>ActiveModel::Validations::ConfirmationValidator</tt>. Now when confirmation validations fail the error will be attached to <tt>:#{attribute}_confirmation</tt> instead of <tt>attribute</tt>.
@@ -54,6 +56,10 @@ Rails 4.0 changed how <tt>assert_generates</tt>, <tt>assert_recognizes</tt>, and
Rails 4.0 also changed the way unicode character routes are drawn. Now you can draw unicode character routes directly. If you already draw such routes, you must change them, e.g. <tt>get Rack::Utils.escape('こんにちは'), :controller => 'welcome', :action => 'index'</tt> to <tt>get 'こんにちは', :controller => 'welcome', :action => 'index'</tt>.
+h4(#active_support4_0). Active Support
+
+Rails 4.0 Removed the `j` alias for `ERB::Util#json_escape` since `j` is already used for `ActionView::Helpers::JavaScriptHelper#escape_javascript`.
+
h4(#helpers_order). Helpers Loading Order
The loading order of helpers from more than one directory has changed in Rails 4.0. Previously, helpers from all directories were gathered and then sorted alphabetically. After upgrade to Rails 4.0 helpers will preserve the order of loaded directories and will be sorted alphabetically only within each directory. Unless you explicitly use <tt>helpers_path</tt> parameter, this change will only impact the way of loading helpers from engines. If you rely on the fact that particular helper from engine loads before or after another helper from application or another engine, you should check if correct methods are available after upgrade. If you would like to change order in which engines are loaded, you can use <tt>config.railties_order=</tt> method.
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 5165d9401f..fea18b5f47 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,5 +1,12 @@
## Rails 4.0.0 (unreleased) ##
+* Correctly handle SCRIPT_NAME when generating routes to engine in application
+ that's mounted at a sub-uri. With this behavior, you *should not* use
+ default_url_options[:script_name] to set proper application's mount point by
+ yourself. *Piotr Sarnacki*
+
+* `config.threadsafe!` is deprecated in favor of `config.eager_load` which provides a more fine grained control on what is eager loaded *José Valim*
+
* The migration generator will now produce AddXXXToYYY/RemoveXXXFromYYY migrations with references statements, for instance
rails g migration AddReferencesToProducts user:references supplier:references{polymorphic}
@@ -31,8 +38,6 @@
* Load all environments available in `config.paths["config/environments"]`. *Piotr Sarnacki*
-* The application generator generates `public/humans.txt` with some basic data. *Paul Campbell*
-
* Add `config.queue_consumer` to allow the default consumer to be configurable. *Carlos Antonio da Silva*
* Add Rails.queue as an interface with a default implementation that consumes jobs in a separate thread. *Yehuda Katz*
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 3531728421..80b8e4b9ba 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -16,7 +16,7 @@ module Rails
#
# Besides providing the same configuration as Rails::Engine and Rails::Railtie,
# the application object has several specific configurations, for example
- # "allow_concurrency", "cache_classes", "consider_all_requests_local", "filter_parameters",
+ # "cache_classes", "consider_all_requests_local", "filter_parameters",
# "logger" and so forth.
#
# Check Rails::Application::Configuration to see them all.
@@ -46,7 +46,7 @@ module Rails
# One by one, each engine sets up its load paths, routes and runs its config/initializers/* files.
# 9) Custom Railtie#initializers added by railties, engines and applications are executed
# 10) Build the middleware stack and run to_prepare callbacks
- # 11) Run config.before_eager_load and eager_load if cache classes is true
+ # 11) Run config.before_eager_load and eager_load! if eager_load is true
# 12) Run config.after_initialize callbacks
#
class Application < Engine
@@ -216,8 +216,9 @@ module Rails
railties.each { |r| r.run_tasks_blocks(app) }
super
require "rails/tasks"
+ config = self.config
task :environment do
- $rails_rake_task = true
+ config.eager_load = false
require_environment!
end
end
@@ -296,7 +297,7 @@ module Rails
middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control
end
- middleware.use ::Rack::Lock unless config.allow_concurrency
+ middleware.use ::Rack::Lock unless config.cache_classes
middleware.use ::Rack::Runtime
middleware.use ::Rack::MethodOverride
middleware.use ::ActionDispatch::RequestId
diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb
index e567df7162..a1bc95550b 100644
--- a/railties/lib/rails/application/bootstrap.rb
+++ b/railties/lib/rails/application/bootstrap.rb
@@ -13,11 +13,18 @@ module Rails
require "active_support/all" unless config.active_support.bare
end
- # Preload all frameworks specified by the Configuration#frameworks.
- # Used by Passenger to ensure everything's loaded before forking and
- # to avoid autoload race conditions in JRuby.
- initializer :preload_frameworks, :group => :all do
- ActiveSupport::Autoload.eager_autoload! if config.preload_frameworks
+ initializer :set_eager_load, :group => :all do
+ if config.eager_load.nil?
+ warn <<-INFO
+config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:
+
+ * development - set it to false
+ * test - set it to false (unless you use a tool that preloads your test environment)
+ * production - set it to true
+
+INFO
+ config.eager_load = config.cache_classes
+ end
end
# Initialize the logger early in the stack in case we need to log some deprecation.
@@ -60,7 +67,6 @@ module Rails
end
# Sets the dependency loading mechanism.
- # TODO: Remove files from the $" and always use require.
initializer :initialize_dependency_mechanism, :group => :all do
ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
end
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 9521805778..0202e86f32 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -5,11 +5,11 @@ require 'rails/engine/configuration'
module Rails
class Application
class Configuration < ::Rails::Engine::Configuration
- attr_accessor :allow_concurrency, :asset_host, :asset_path, :assets, :autoflush_log,
+ attr_accessor :asset_host, :asset_path, :assets, :autoflush_log,
:cache_classes, :cache_store, :consider_all_requests_local, :console,
- :dependency_loading, :exceptions_app, :file_watcher, :filter_parameters,
+ :eager_load, :exceptions_app, :file_watcher, :filter_parameters,
:force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags,
- :preload_frameworks, :railties_order, :relative_url_root, :secret_token,
+ :railties_order, :relative_url_root, :secret_token,
:serve_static_assets, :ssl_options, :static_cache_control, :session_options,
:time_zone, :reload_classes_only_on_change,
:queue, :queue_consumer
@@ -20,11 +20,9 @@ module Rails
def initialize(*)
super
self.encoding = "utf-8"
- @allow_concurrency = false
@consider_all_requests_local = false
@filter_parameters = []
@helpers_paths = []
- @dependency_loading = true
@serve_static_assets = true
@static_cache_control = nil
@force_ssl = false
@@ -45,11 +43,12 @@ module Rails
@log_formatter = ActiveSupport::Logger::SimpleFormatter.new
@queue = Rails::Queueing::Queue
@queue_consumer = Rails::Queueing::ThreadedConsumer
+ @eager_load = nil
@assets = ActiveSupport::OrderedOptions.new
@assets.enabled = false
@assets.paths = []
- @assets.precompile = [ Proc.new{ |path| !File.extname(path).in?(['.js', '.css']) },
+ @assets.precompile = [ Proc.new { |path| !%w(.js .css).include?(File.extname(path)) },
/(?:\/|\\|\A)application\.(css|js)$/ ]
@assets.prefix = "/assets"
@assets.version = ''
@@ -91,15 +90,12 @@ module Rails
end
end
- # Enable threaded mode. Allows concurrent requests to controller actions and
- # multiple database connections. Also disables automatic dependency loading
- # after boot, and disables reloading code on every request, as these are
- # fundamentally incompatible with thread safety.
def threadsafe!
- @preload_frameworks = true
+ ActiveSupport::Deprecation.warn "config.threadsafe! is deprecated. Rails applications " \
+ "behave by default as thread safe in production as long as config.cache_classes and " \
+ "config.eager_load are set to true"
@cache_classes = true
- @dependency_loading = false
- @allow_concurrency = true
+ @eager_load = true
self
end
@@ -130,7 +126,12 @@ module Rails
when :disabled
nil
when :active_record_store
- ActiveRecord::SessionStore
+ begin
+ ActionDispatch::Session::ActiveRecordStore
+ rescue NameError
+ raise "`ActiveRecord::SessionStore` is extracted out of Rails into a gem. " \
+ "Please add `activerecord-session_store` to your Gemfile to use it."
+ end
when Symbol
ActionDispatch::Session.const_get(@session_store.to_s.camelize)
else
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index 60aa40b92f..777f0d269e 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -50,9 +50,9 @@ module Rails
end
initializer :eager_load! do
- if config.cache_classes && !(defined?($rails_rake_task) && $rails_rake_task)
+ if config.eager_load
ActiveSupport.run_load_hooks(:before_eager_load, self)
- eager_load!
+ config.eager_load_namespaces.each(&:eager_load!)
end
end
@@ -91,7 +91,7 @@ module Rails
# Disable dependency loading during request cycle
initializer :disable_dependency_loading do
- if config.cache_classes && !config.dependency_loading
+ if config.eager_load && config.cache_classes
ActiveSupport::Dependencies.unhook!
end
end
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index 8816387d34..9c5dc8f188 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/object/inclusion'
-
ARGV << '--help' if ARGV.empty?
aliases = {
@@ -71,7 +69,7 @@ when 'application', 'runner'
require "rails/commands/#{command}"
when 'new'
- if ARGV.first.in?(['-h', '--help'])
+ if %w(-h --help).include?(ARGV.first)
require 'rails/commands/application'
else
puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
@@ -84,7 +82,7 @@ when '--version', '-v'
require 'rails/commands/application'
else
- puts "Error: Command not recognized" unless command.in?(['-h', '--help'])
+ puts "Error: Command not recognized" unless %w(-h --help).include?(command)
puts <<-EOT
Usage: rails COMMAND [ARGS]
diff --git a/railties/lib/rails/commands/destroy.rb b/railties/lib/rails/commands/destroy.rb
index ae354eca97..9023c61bf2 100644
--- a/railties/lib/rails/commands/destroy.rb
+++ b/railties/lib/rails/commands/destroy.rb
@@ -1,7 +1,6 @@
require 'rails/generators'
-require 'active_support/core_ext/object/inclusion'
-if ARGV.first.in?([nil, "-h", "--help"])
+if [nil, "-h", "--help"].include?(ARGV.first)
Rails::Generators.help 'destroy'
exit
end
diff --git a/railties/lib/rails/commands/generate.rb b/railties/lib/rails/commands/generate.rb
index 1fb2d98834..9f13cb0513 100644
--- a/railties/lib/rails/commands/generate.rb
+++ b/railties/lib/rails/commands/generate.rb
@@ -1,7 +1,6 @@
require 'rails/generators'
-require 'active_support/core_ext/object/inclusion'
-if ARGV.first.in?([nil, "-h", "--help"])
+if [nil, "-h", "--help"].include?(ARGV.first)
Rails::Generators.help 'generate'
exit
end
diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb
index 9ef64da3ef..a684129353 100644
--- a/railties/lib/rails/commands/server.rb
+++ b/railties/lib/rails/commands/server.rb
@@ -87,6 +87,15 @@ module Rails
middlewares = []
middlewares << [Rails::Rack::Debugger] if options[:debugger]
middlewares << [::Rack::ContentLength]
+
+ # FIXME: add Rack::Lock in the case people are using webrick.
+ # This is to remain backwards compatible for those who are
+ # running webrick in production. We should consider removing this
+ # in development.
+ if server.name == 'Rack::Handler::WEBrick'
+ middlewares << [::Rack::Lock]
+ end
+
Hash.new(middlewares)
end
diff --git a/railties/lib/rails/console/app.rb b/railties/lib/rails/console/app.rb
index ee8bb55f38..2a69c26deb 100644
--- a/railties/lib/rails/console/app.rb
+++ b/railties/lib/rails/console/app.rb
@@ -1,9 +1,6 @@
require 'active_support/all'
-require 'active_support/test_case'
require 'action_controller'
-# work around the at_exit hook in test/unit, which kills IRB
-
module Rails
module ConsoleMethods
# reference the global "app" instance, created on demand. To recreate the
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index f469c334a7..3a5caf9f62 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -339,11 +339,16 @@ module Rails
class << self
attr_accessor :called_from, :isolated
+
alias :isolated? :isolated
alias :engine_name :railtie_name
+ delegate :eager_load!, to: :instance
+
def inherited(base)
unless base.abstract_railtie?
+ Rails::Railtie::Configuration.eager_load_namespaces << base
+
base.called_from = begin
# Remove the line number from backtraces making sure we don't leave anything behind
call_stack = caller.map { |p| p.sub(/:\d+.*/, '') }
@@ -494,7 +499,11 @@ module Rails
# Define the Rack API for this engine.
def call(env)
- app.call(env.merge!(env_config))
+ env.merge!(env_config)
+ if env['SCRIPT_NAME']
+ env.merge! "ROUTES_#{routes.object_id}_SCRIPT_NAME" => env['SCRIPT_NAME'].dup
+ end
+ app.call(env)
end
# Defines additional Rack env configuration that is added on each call.
diff --git a/railties/lib/rails/engine/commands.rb b/railties/lib/rails/engine/commands.rb
index ffbc0b4bd6..072d16291b 100644
--- a/railties/lib/rails/engine/commands.rb
+++ b/railties/lib/rails/engine/commands.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/object/inclusion'
-
ARGV << '--help' if ARGV.empty?
aliases = {
@@ -25,7 +23,7 @@ when '--version', '-v'
require 'rails/commands/application'
else
- puts "Error: Command not recognized" unless command.in?(['-h', '--help'])
+ puts "Error: Command not recognized" unless %w(-h --help).include?(command)
puts <<-EOT
Usage: rails COMMAND [ARGS]
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 4fa990171d..a8c0626a41 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -173,7 +173,6 @@ module Rails
"#{orm}:migration",
"#{orm}:model",
"#{orm}:observer",
- "#{orm}:session_migration",
"#{test}:controller",
"#{test}:helper",
"#{test}:integration",
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 5838c9fc38..383bea9e54 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -140,14 +140,14 @@ module Rails
gem 'rails', path: '#{Rails::Generators::RAILS_DEV_PATH}'
gem 'journey', github: 'rails/journey'
gem 'arel', github: 'rails/arel'
- gem 'active_record_deprecated_finders', github: 'rails/active_record_deprecated_finders'
+ gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders'
GEMFILE
elsif options.edge?
<<-GEMFILE.strip_heredoc
gem 'rails', github: 'rails/rails'
gem 'journey', github: 'rails/journey'
gem 'arel', github: 'rails/arel'
- gem 'active_record_deprecated_finders', github: 'rails/active_record_deprecated_finders'
+ gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders'
GEMFILE
else
<<-GEMFILE.strip_heredoc
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index a49a1724c5..a3f8ebf476 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -8,7 +8,6 @@ rescue LoadError
end
require 'rails/generators/actions'
-require 'active_support/core_ext/object/inclusion'
module Rails
module Generators
@@ -171,7 +170,7 @@ module Rails
names.each do |name|
defaults = if options[:type] == :boolean
{ }
- elsif default_value_for_option(name, options).in?([true, false])
+ elsif [true, false].include?(default_value_for_option(name, options))
{ :banner => "" }
else
{ :desc => "#{name.to_s.humanize} to be invoked", :banner => "NAME" }
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index 5915d20010..1ac0248bcf 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -27,9 +27,6 @@ module <%= app_const_base %>
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
- # Activate observers that should always be running.
- # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
-
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
@@ -54,11 +51,6 @@ module <%= app_const_base %>
# in your app. As such, your models will need to explicitly whitelist or blacklist accessible
# parameters by using an attr_accessible or attr_protected declaration.
<%= comment_if :skip_active_record %>config.active_record.whitelist_attributes = true
-
- # Specifies whether or not has_many or has_one association option :dependent => :restrict raises
- # an exception. If set to true, then an ActiveRecord::DeleteRestrictionError exception would be
- # raised. If set to false, then an error will be added on the model instead.
- <%= comment_if :skip_active_record %>config.active_record.dependent_restrict_raises = false
<% unless options.skip_sprockets? -%>
# Enable the asset pipeline.
@@ -68,7 +60,7 @@ module <%= app_const_base %>
config.assets.version = '1.0'
<% end -%>
- # Enable app-wide asynchronous ActionMailer
+ # Enable app-wide asynchronous ActionMailer.
# config.action_mailer.async = true
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environment.rb b/railties/lib/rails/generators/rails/app/templates/config/environment.rb
index 1684986a59..e080ebd74e 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environment.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/environment.rb
@@ -1,5 +1,5 @@
-# Load the rails application
+# Load the rails application.
require File.expand_path('../application', __FILE__)
-# Initialize the rails application
+# Initialize the rails application.
<%= app_const %>.initialize!
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index fee9eb9456..122e7e2b34 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -6,6 +6,9 @@
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
+ # Do not eager load code on boot.
+ config.eager_load = false
+
# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
@@ -39,6 +42,6 @@
config.assets.debug = true
<%- end -%>
- # In development, use an in-memory queue for queueing
+ # In development, use an in-memory queue for queueing.
config.queue = Rails::Queueing::Queue
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index 072aa8355d..a627636089 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -4,6 +4,12 @@
# Code is not reloaded between requests.
config.cache_classes = true
+ # Eager load code on boot. This eager loads most of Rails and
+ # your application in memory, allowing both thread web servers
+ # and those relying on copy on write to perform better.
+ # Rake tasks automatically ignore this option for performance.
+ config.eager_load = true
+
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
@@ -20,9 +26,6 @@
# Generate digests for assets URLs.
config.assets.digest = true
-
- # Defaults to nil and saved in location specified by config.assets.prefix
- # config.assets.manifest = YOUR_PATH
<%- end -%>
# Specifies the header that your server uses for sending files.
@@ -74,10 +77,10 @@
# Disable automatic flushing of the log to improve performance.
# config.autoflush_log = false
- # Use default logging formatter so that PID and timestamp are not suppressed
+ # Use default logging formatter so that PID and timestamp are not suppressed.
config.log_formatter = ::Logger::Formatter.new
# Default the production mode queue to an in-memory queue. You will probably
- # want to replace this with an out-of-process queueing solution
+ # want to replace this with an out-of-process queueing solution.
config.queue = Rails::Queueing::Queue
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
index b27b88a3c6..8ab27eb6e1 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
@@ -7,6 +7,11 @@
# and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
+ # Do not eager load code on boot. This avoids loading your whole application
+ # just for the purpose of running a single test. If you are using a tool that
+ # preloads Rails for running tests, you may have to set it to true.
+ config.eager_load = false
+
# Configure static asset server for tests with Cache-Control for performance.
config.serve_static_assets = true
config.static_cache_control = "public, max-age=3600"
@@ -34,6 +39,6 @@
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
- # Use the testing queue
+ # Use the testing queue.
config.queue = Rails::Queueing::TestQueue
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt
index e02397aaf9..3c5611ca59 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/secret_token.rb.tt
@@ -2,8 +2,11 @@
# Your secret key for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
+
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
+# You can use `rake secret` to generate a secure secret key.
+
# Make sure your secret_token is kept private
# if you're sharing your code publicly.
<%= app_const %>.config.secret_token = '<%= app_secret %>'
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
index ade0c4f78c..4a099a4ce2 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
@@ -1,8 +1,3 @@
# Be sure to restart your server when you modify this file.
<%= app_const %>.config.session_store :cookie_store, key: <%= "'_#{app_name}_session'" %>
-
-# Use the database for sessions instead of the cookie-based default,
-# which shouldn't be used to store highly confidential information
-# (create the session table with "rails generate session_migration")
-# <%= app_const %>.config.session_store :active_record_store
diff --git a/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml b/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml
index 179c14ca52..0653957166 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/locales/en.yml
@@ -1,5 +1,23 @@
-# Sample localization file for English. Add more files in this directory for other locales.
-# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
+# Files in the config/locales directory are used for internationalization
+# and are automatically loaded by Rails. If you want to use locales other
+# than English, add the necessary files in this directory.
+#
+# To use the locales, use `I18n.t`:
+#
+# I18n.t 'hello'
+#
+# In views, this is aliased to just `t`:
+#
+# <%= t('hello') %>
+#
+# To use a different locale, set it with `I18n.locale`:
+#
+# I18n.locale = :es
+#
+# This would use the information in config/locales/es.yml.
+#
+# To learn more, please read the Rails Internationalization guide
+# available at http://guides.rubyonrails.org/i18n.html.
en:
hello: "Hello world"
diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
index 85acac6725..f6b1ef1feb 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
@@ -8,11 +8,11 @@
# Sample of regular route:
# get 'products/:id' => 'catalog#view'
- # Keep in mind you can assign values other than :controller and :action
+ # Keep in mind you can assign values other than :controller and :action.
# Sample of named route:
# get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
- # This route can be invoked with purchase_url(id: product.id)
+ # This route can be invoked with purchase_url(id: product.id).
# Sample resource route (maps HTTP verbs to controller actions automatically):
# resources :products
@@ -35,7 +35,7 @@
# resource :seller
# end
- # Sample resource route with more complex sub-resources
+ # Sample resource route with more complex sub-resources:
# resources :products do
# resources :comments
# resources :sales do
@@ -51,5 +51,5 @@
# end
- # See how all your routes lay out with "rake routes"
+ # See how all your routes lay out with "rake routes".
end
diff --git a/railties/lib/rails/generators/rails/app/templates/public/humans.txt.tt b/railties/lib/rails/generators/rails/app/templates/public/humans.txt.tt
deleted file mode 100644
index f081e08b6c..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/public/humans.txt.tt
+++ /dev/null
@@ -1,7 +0,0 @@
-# See more about this file at: http://humanstxt.org/
-# For format suggestions, see: http://humanstxt.org/Standard.html
-/* TEAM */
-
-/* APP */
- Name: <%= app_const_base %>
- Software: Ruby on Rails
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 ab0e440bc4..4f937ad65a 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
@@ -155,7 +155,7 @@ task :default => :test
:desc => "Create dummy application at given path"
class_option :full, :type => :boolean, :default => false,
- :desc => "Generate rails engine with integration tests"
+ :desc => "Generate a rails engine with bundled Rails application for testing"
class_option :mountable, :type => :boolean, :default => false,
:desc => "Generate mountable isolated application"
diff --git a/railties/lib/rails/generators/rails/session_migration/USAGE b/railties/lib/rails/generators/rails/session_migration/USAGE
deleted file mode 100644
index 564d1ffd78..0000000000
--- a/railties/lib/rails/generators/rails/session_migration/USAGE
+++ /dev/null
@@ -1,8 +0,0 @@
-Description:
- Creates a migration to add the sessions table used by the ORM session store.
- Pass the migration name, either CamelCased or under_scored, as an argument.
-
- Before invoking this generator, be sure that your ORM supports session stores.
-
-Example:
- `rails generate session_migration CreateSessionTable`
diff --git a/railties/lib/rails/generators/rails/session_migration/session_migration_generator.rb b/railties/lib/rails/generators/rails/session_migration/session_migration_generator.rb
deleted file mode 100644
index 258cc5b4a0..0000000000
--- a/railties/lib/rails/generators/rails/session_migration/session_migration_generator.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module Rails
- module Generators
- class SessionMigrationGenerator < NamedBase #metagenerator
- argument :name, :type => :string, :default => "add_sessions_table"
- hook_for :orm, :required => true
- end
- end
-end
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index 1cb99463cc..fba685c769 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -178,9 +178,6 @@ module Rails
@config ||= Railtie::Configuration.new
end
- def eager_load!
- end
-
def railtie_namespace
@railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) }
end
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
index 1c6b3769a5..9dc8843887 100644
--- a/railties/lib/rails/railtie/configuration.rb
+++ b/railties/lib/rails/railtie/configuration.rb
@@ -7,6 +7,16 @@ module Rails
@@options ||= {}
end
+ # Expose the eager_load_namespaces at "module" level for convenience.
+ def self.eager_load_namespaces #:nodoc:
+ @@eager_load_namespaces ||= []
+ end
+
+ # All namespaces that are eager loaded
+ def eager_load_namespaces
+ @@eager_load_namespaces ||= []
+ end
+
# Add files that should be watched for change.
def watchable_files
@@watchable_files ||= []
diff --git a/railties/lib/rails/tasks/tmp.rake b/railties/lib/rails/tasks/tmp.rake
index 0d6c10328f..0968765b4f 100644
--- a/railties/lib/rails/tasks/tmp.rake
+++ b/railties/lib/rails/tasks/tmp.rake
@@ -2,10 +2,16 @@ namespace :tmp do
desc "Clear session, cache, and socket files from tmp/ (narrow w/ tmp:sessions:clear, tmp:cache:clear, tmp:sockets:clear)"
task :clear => [ "tmp:sessions:clear", "tmp:cache:clear", "tmp:sockets:clear"]
+ tmp_dirs = [ 'tmp/sessions',
+ 'tmp/cache',
+ 'tmp/sockets',
+ 'tmp/pids',
+ 'tmp/cache/assets' ]
+
+ tmp_dirs.each { |d| directory d }
+
desc "Creates tmp directories for sessions, cache, sockets, and pids"
- task :create do
- FileUtils.mkdir_p(%w( tmp/sessions tmp/cache tmp/sockets tmp/pids tmp/cache/assets ))
- end
+ task :create => tmp_dirs
namespace :sessions do
# desc "Clears all files in tmp/sessions"
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index dd3af86b99..c29fa4d95f 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -106,20 +106,28 @@ module ApplicationTests
precompile!
images_should_compile.each do |filename|
- assert File.exists?("#{app_path}/public/assets/#{filename}")
+ assert_file_exists "#{app_path}/public/assets/#{filename}"
end
- assert File.exists?("#{app_path}/public/assets/application.js")
- assert File.exists?("#{app_path}/public/assets/application.css")
+ assert_file_exists("#{app_path}/public/assets/application.js")
+ assert_file_exists("#{app_path}/public/assets/application.css")
+
+ assert_no_file_exists("#{app_path}/public/assets/someapplication.js")
+ assert_no_file_exists("#{app_path}/public/assets/someapplication.css")
- assert !File.exists?("#{app_path}/public/assets/someapplication.js")
- assert !File.exists?("#{app_path}/public/assets/someapplication.css")
+ assert_no_file_exists("#{app_path}/public/assets/something.min.js")
+ assert_no_file_exists("#{app_path}/public/assets/something.min.css")
- assert !File.exists?("#{app_path}/public/assets/something.min.js")
- assert !File.exists?("#{app_path}/public/assets/something.min.css")
+ assert_no_file_exists("#{app_path}/public/assets/something.else.js")
+ assert_no_file_exists("#{app_path}/public/assets/something.else.css")
+ end
+
+ def assert_file_exists(filename)
+ assert File.exists?(filename), "missing #{filename}"
+ end
- assert !File.exists?("#{app_path}/public/assets/something.else.js")
- assert !File.exists?("#{app_path}/public/assets/something.else.css")
+ def assert_no_file_exists(filename)
+ assert !File.exists?(filename), "#{filename} does exist"
end
test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index c813defe93..039b08c723 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -139,22 +139,19 @@ module ApplicationTests
assert_instance_of Pathname, Rails.root
end
- test "marking the application as threadsafe sets the correct config variables" do
+ test "initialize an eager loaded, cache classes app" do
add_to_config <<-RUBY
- config.threadsafe!
+ config.eager_load = true
+ config.cache_classes = true
RUBY
require "#{app_path}/config/application"
- assert AppTemplate::Application.config.allow_concurrency
+ assert AppTemplate::Application.initialize!
end
- test "initialize a threadsafe app" do
- add_to_config <<-RUBY
- config.threadsafe!
- RUBY
-
+ test "application is always added to eager_load namespaces" do
require "#{app_path}/config/application"
- assert AppTemplate::Application.initialize!
+ assert AppTemplate::Application, AppTemplate::Application.config.eager_load_namespaces
end
test "asset_path defaults to nil for application" do
@@ -162,10 +159,11 @@ module ApplicationTests
assert_equal nil, AppTemplate::Application.config.asset_path
end
- test "the application can be marked as threadsafe when there are no frameworks" do
+ test "the application can be eager loaded even when there are no frameworks" do
FileUtils.rm_rf("#{app_path}/config/environments")
add_to_config <<-RUBY
- config.threadsafe!
+ config.eager_load = true
+ config.cache_classes = true
RUBY
use_frameworks []
@@ -175,22 +173,6 @@ module ApplicationTests
end
end
- test "frameworks are not preloaded by default" do
- require "#{app_path}/config/environment"
-
- assert ActionController.autoload?(:Caching)
- end
-
- test "frameworks are preloaded with config.preload_frameworks is set" do
- add_to_config <<-RUBY
- config.preload_frameworks = true
- RUBY
-
- require "#{app_path}/config/environment"
-
- assert !ActionView.autoload?(:AssetPaths)
- end
-
test "filter_parameters should be able to set via config.filter_parameters" do
add_to_config <<-RUBY
config.filter_parameters += [ :foo, 'bar', lambda { |key, value|
@@ -648,5 +630,24 @@ module ApplicationTests
ActiveRecord::Base
assert defined?(FooObserver)
end
+
+ test "config.session_store with :active_record_store with activerecord-session_store gem" do
+ begin
+ make_basic_app do |app|
+ ActionDispatch::Session::ActiveRecordStore = Class.new(ActionDispatch::Session::CookieStore)
+ app.config.session_store :active_record_store
+ end
+ ensure
+ ActionDispatch::Session.send :remove_const, :ActiveRecordStore
+ end
+ end
+
+ test "config.session_store with :active_record_store without activerecord-session_store gem" do
+ assert_raise RuntimeError, /activerecord-session_store/ do
+ make_basic_app do |app|
+ app.config.session_store :active_record_store
+ end
+ end
+ end
end
end
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index c2d4a0f2c8..d3bbac811c 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -163,26 +163,6 @@ module ApplicationTests
end
# AR
- test "database middleware doesn't initialize when session store is not active_record" do
- add_to_config <<-RUBY
- config.root = "#{app_path}"
- config.session_store :cookie_store, { :key => "blahblahblah" }
- RUBY
- require "#{app_path}/config/environment"
-
- assert !Rails.application.config.middleware.include?(ActiveRecord::SessionStore)
- end
-
- test "database middleware initializes when session store is active record" do
- add_to_config "config.session_store :active_record_store"
-
- require "#{app_path}/config/environment"
-
- expects = [ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActiveRecord::SessionStore]
- middleware = Rails.application.config.middleware.map { |m| m.klass }
- assert_equal expects, middleware & expects
- end
-
test "active_record extensions are applied to ActiveRecord" do
add_to_config "config.active_record.table_name_prefix = 'tbl_'"
require "#{app_path}/config/environment"
diff --git a/railties/test/application/initializers/hooks_test.rb b/railties/test/application/initializers/hooks_test.rb
index 7b3e6844fd..7c48696672 100644
--- a/railties/test/application/initializers/hooks_test.rb
+++ b/railties/test/application/initializers/hooks_test.rb
@@ -20,11 +20,11 @@ module ApplicationTests
assert $foo
end
- test "hooks block works correctly without cache classes (before_eager_load is not called)" do
+ test "hooks block works correctly without eager_load (before_eager_load is not called)" do
add_to_config <<-RUBY
$initialization_callbacks = []
config.root = "#{app_path}"
- config.cache_classes = false
+ config.eager_load = false
config.before_configuration { $initialization_callbacks << 1 }
config.before_initialize { $initialization_callbacks << 2 }
config.before_eager_load { Boom }
@@ -35,11 +35,11 @@ module ApplicationTests
assert_equal [1,2,3], $initialization_callbacks
end
- test "hooks block works correctly with cache classes" do
+ test "hooks block works correctly with eager_load" do
add_to_config <<-RUBY
$initialization_callbacks = []
config.root = "#{app_path}"
- config.cache_classes = true
+ config.eager_load = true
config.before_configuration { $initialization_callbacks << 1 }
config.before_initialize { $initialization_callbacks << 2 }
config.before_eager_load { $initialization_callbacks << 3 }
diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb
index d1a614e181..42096cfec4 100644
--- a/railties/test/application/middleware/exceptions_test.rb
+++ b/railties/test/application/middleware/exceptions_test.rb
@@ -88,9 +88,6 @@ module ApplicationTests
end
test "displays diagnostics message when exception raised in template that contains UTF-8" do
- app.config.action_dispatch.show_exceptions = true
- app.config.consider_all_requests_local = true
-
controller :foo, <<-RUBY
class FooController < ActionController::Base
def index
@@ -98,18 +95,15 @@ module ApplicationTests
end
RUBY
+ app.config.action_dispatch.show_exceptions = true
+ app.config.consider_all_requests_local = true
+
app_file 'app/views/foo/index.html.erb', <<-ERB
<% raise 'boooom' %>
✓測試テスト시험
ERB
- app_file 'config/routes.rb', <<-RUBY
- AppTemplate::Application.routes.draw do
- post ':controller(/:action)'
- end
- RUBY
-
- post '/foo', :utf8 => '✓'
+ get '/foo', :utf8 => '✓'
assert_match(/boooom/, last_response.body)
assert_match(/測試テスト시험/, last_response.body)
end
diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb
index 066f0c1c84..9d97bae9ae 100644
--- a/railties/test/application/middleware/remote_ip_test.rb
+++ b/railties/test/application/middleware/remote_ip_test.rb
@@ -4,20 +4,6 @@ module ApplicationTests
class RemoteIpTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation
- def setup
- build_app
- boot_rails
- FileUtils.rm_rf "#{app_path}/config/environments"
- end
-
- def teardown
- teardown_app
- end
-
- def app
- @app ||= Rails.application
- end
-
def remote_ip(env = {})
remote_ip = nil
env = Rack::MockRequest.env_for("/").merge(env).merge!(
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 9c9ed0cd6b..34b460d0a7 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -87,8 +87,8 @@ module ApplicationTests
assert !middleware.include?("ActiveRecord::QueryCache")
end
- test "removes lock if allow concurrency is set" do
- add_to_config "config.allow_concurrency = true"
+ test "removes lock if cache classes is set" do
+ add_to_config "config.cache_classes = true"
boot!
assert !middleware.include?("Rack::Lock")
end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index a450f90dbf..b05fe3aed5 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -55,6 +55,29 @@ module ApplicationTests
assert_match "Doing something...", output
end
+ def test_does_not_explode_when_accessing_a_model_with_eager_load
+ add_to_config <<-RUBY
+ config.eager_load = true
+
+ rake_tasks do
+ task :do_nothing => :environment do
+ Hello.new.world
+ end
+ end
+ RUBY
+
+ app_file "app/models/hello.rb", <<-RUBY
+ class Hello
+ def world
+ puts "Hello world"
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path){ `rake do_nothing` }
+ assert_match "Hello world", output
+ end
+
def test_code_statistics_sanity
assert_match "Code LOC: 5 Test LOC: 0 Code to Test Ratio: 1:0.0",
Dir.chdir(app_path){ `rake stats` }
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 5534476a6d..c294bfb238 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -213,7 +213,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_no_file "config/database.yml"
assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
assert_file "config/application.rb", /#\s+config\.active_record\.whitelist_attributes = true/
- assert_file "config/application.rb", /#\s+config\.active_record\.dependent_restrict_raises = false/
assert_file "test/test_helper.rb" do |helper_content|
assert_no_match(/fixtures :all/, helper_content)
end
@@ -367,21 +366,11 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "config/application.rb", /config\.active_record\.whitelist_attributes = true/
end
- def test_active_record_dependent_restrict_raises_is_present_application_config
- run_generator
- assert_file "config/application.rb", /config\.active_record\.dependent_restrict_raises = false/
- end
-
def test_pretend_option
output = run_generator [File.join(destination_root, "myapp"), "--pretend"]
assert_no_match(/run bundle install/, output)
end
- def test_humans_txt_file
- run_generator [File.join(destination_root, 'things-43')]
- assert_file "things-43/public/humans.txt", /Name: Things43/, /Software: Ruby on Rails/
- end
-
protected
def action(*args, &block)
diff --git a/railties/test/generators/session_migration_generator_test.rb b/railties/test/generators/session_migration_generator_test.rb
deleted file mode 100644
index b590047ff0..0000000000
--- a/railties/test/generators/session_migration_generator_test.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require 'generators/generators_test_helper'
-require 'rails/generators/rails/session_migration/session_migration_generator'
-
-class SessionMigrationGeneratorTest < Rails::Generators::TestCase
- include GeneratorsTestHelper
-
- def test_session_migration_with_default_name
- run_generator
- assert_migration "db/migrate/add_sessions_table.rb", /class AddSessionsTable < ActiveRecord::Migration/
- end
-
- def test_session_migration_with_given_name
- run_generator ["create_session_table"]
- assert_migration "db/migrate/create_session_table.rb", /class CreateSessionTable < ActiveRecord::Migration/
- end
-
- def test_session_migration_with_custom_table_name
- ActiveRecord::SessionStore::Session.table_name = "custom_table_name"
- run_generator
- assert_migration "db/migrate/add_sessions_table.rb" do |migration|
- assert_match(/class AddSessionsTable < ActiveRecord::Migration/, migration)
- assert_match(/create_table :custom_table_name/, migration)
- end
- ensure
- ActiveRecord::SessionStore::Session.table_name = "sessions"
- end
-end
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 6071cd3f39..37839a1c54 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -12,7 +12,6 @@ require 'bundler/setup'
require 'minitest/autorun'
require 'active_support/test_case'
-# TODO: Remove setting this magic constant
RAILS_FRAMEWORK_ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../..")
# These files do not require any others and are needed
@@ -118,6 +117,7 @@ module TestHelpers
end
add_to_config <<-RUBY
+ config.eager_load = false
config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
config.session_store :cookie_store, :key => "_myapp_session"
config.active_support.deprecation = :log
@@ -136,6 +136,7 @@ module TestHelpers
require "action_controller/railtie"
app = Class.new(Rails::Application)
+ app.config.eager_load = false
app.config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
app.config.session_store :cookie_store, :key => "_myapp_session"
app.config.active_support.deprecation = :log
@@ -253,7 +254,6 @@ module TestHelpers
:activerecord] - arr
if to_remove.include? :activerecord
remove_from_config "config.active_record.whitelist_attributes = true"
- remove_from_config "config.active_record.dependent_restrict_raises = false"
end
$:.reject! {|path| path =~ %r'/(#{to_remove.join('|')})/' }
end
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 63814f7a04..e52b3efdab 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -1193,6 +1193,54 @@ YAML
last_response.body.split("\n").map(&:strip)
end
+ test "paths are properly generated when application is mounted at sub-path" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ isolate_namespace Bukkits
+ end
+ end
+ RUBY
+
+ app_file "app/controllers/bar_controller.rb", <<-RUBY
+ class BarController < ApplicationController
+ def index
+ render :text => bukkits.bukkit_path
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ get '/bar' => 'bar#index', :as => 'bar'
+ mount Bukkits::Engine => "/bukkits", :as => "bukkits"
+ end
+ RUBY
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ get '/bukkit' => 'bukkit#index'
+ end
+ RUBY
+
+
+ @plugin.write "app/controllers/bukkits/bukkit_controller.rb", <<-RUBY
+ class Bukkits::BukkitController < ActionController::Base
+ def index
+ render :text => main_app.bar_path
+ end
+ end
+ RUBY
+
+ boot_rails
+
+ get("/bukkits/bukkit", {}, {'SCRIPT_NAME' => '/foo'})
+ assert_equal '/foo/bar', last_response.body
+
+ get("/bar", {}, {'SCRIPT_NAME' => '/foo'})
+ assert_equal '/foo/bukkits/bukkit', last_response.body
+ end
+
private
def app
Rails.application
diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb
index 4c0fdee556..bd13c3aba3 100644
--- a/railties/test/railties/mounted_engine_test.rb
+++ b/railties/test/railties/mounted_engine_test.rb
@@ -163,24 +163,14 @@ module ApplicationTests
end
end
- def reset_script_name!
- Rails.application.routes.default_url_options = {}
- end
-
- def script_name(script_name)
- Rails.application.routes.default_url_options = {:script_name => script_name}
- end
-
test "routes generation in engine and application" do
# test generating engine's route from engine
get "/john/blog/posts"
assert_equal "/john/blog/posts/1", last_response.body
# test generating engine's route from engine with default_url_options
- script_name "/foo"
get "/john/blog/posts", {}, 'SCRIPT_NAME' => "/foo"
assert_equal "/foo/john/blog/posts/1", last_response.body
- reset_script_name!
# test generating engine's route from application
get "/engine_route"
@@ -193,14 +183,11 @@ module ApplicationTests
assert_equal "/john/blog/posts", last_response.body
# test generating engine's route from application with default_url_options
- script_name "/foo"
get "/engine_route", {}, 'SCRIPT_NAME' => "/foo"
assert_equal "/foo/anonymous/blog/posts", last_response.body
- script_name "/foo"
get "/url_for_engine_route", {}, 'SCRIPT_NAME' => "/foo"
assert_equal "/foo/john/blog/posts", last_response.body
- reset_script_name!
# test generating application's route from engine
get "/someone/blog/generate_application_route"
@@ -210,10 +197,8 @@ module ApplicationTests
assert_equal "/", last_response.body
# test generating application's route from engine with default_url_options
- script_name "/foo"
get "/someone/blog/generate_application_route", {}, 'SCRIPT_NAME' => '/foo'
assert_equal "/foo/", last_response.body
- reset_script_name!
# test polymorphic routes
get "/polymorphic_route"