aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Kemper <jeremy@bitsweat.net>2009-01-10 12:14:44 -0800
committerJeremy Kemper <jeremy@bitsweat.net>2009-01-10 12:14:44 -0800
commit223a1d9451c88800e9fcc93a726fdebec99e2650 (patch)
tree207b0b671778ac7a0e00829f6f642256b261cb28
parent13c6c3cfc59ff0b400b294dce15f32752b0fb5f5 (diff)
parent9fe69b225cfbf12c02ee1433adf3a5aa17bcdf59 (diff)
downloadrails-223a1d9451c88800e9fcc93a726fdebec99e2650.tar.gz
rails-223a1d9451c88800e9fcc93a726fdebec99e2650.tar.bz2
rails-223a1d9451c88800e9fcc93a726fdebec99e2650.zip
Merge branch 'master' into savepoints
-rw-r--r--actionmailer/CHANGELOG16
-rw-r--r--actionmailer/lib/action_mailer/base.rb5
-rw-r--r--actionmailer/test/abstract_unit.rb8
-rw-r--r--actionmailer/test/mail_service_test.rb6
-rw-r--r--actionpack/CHANGELOG768
-rw-r--r--actionpack/Rakefile2
-rw-r--r--actionpack/lib/action_controller.rb31
-rw-r--r--actionpack/lib/action_controller/assertions/response_assertions.rb56
-rw-r--r--actionpack/lib/action_controller/assertions/routing_assertions.rb2
-rw-r--r--actionpack/lib/action_controller/assertions/selector_assertions.rb3
-rw-r--r--actionpack/lib/action_controller/base.rb111
-rw-r--r--actionpack/lib/action_controller/benchmarking.rb18
-rw-r--r--actionpack/lib/action_controller/caching.rb3
-rw-r--r--actionpack/lib/action_controller/caching/actions.rb2
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb2
-rw-r--r--actionpack/lib/action_controller/caching/sql_cache.rb18
-rw-r--r--actionpack/lib/action_controller/cgi_ext.rb1
-rw-r--r--actionpack/lib/action_controller/cgi_ext/session.rb53
-rw-r--r--actionpack/lib/action_controller/cgi_process.rb2
-rw-r--r--actionpack/lib/action_controller/cookies.rb36
-rw-r--r--actionpack/lib/action_controller/dispatcher.rb55
-rw-r--r--actionpack/lib/action_controller/failsafe.rb6
-rw-r--r--actionpack/lib/action_controller/flash.rb73
-rw-r--r--actionpack/lib/action_controller/helpers.rb6
-rw-r--r--actionpack/lib/action_controller/http_authentication.rb191
-rw-r--r--actionpack/lib/action_controller/integration.rb147
-rw-r--r--actionpack/lib/action_controller/layout.rb22
-rw-r--r--actionpack/lib/action_controller/lock.rb16
-rw-r--r--actionpack/lib/action_controller/middleware_stack.rb74
-rw-r--r--actionpack/lib/action_controller/middlewares.rb21
-rw-r--r--actionpack/lib/action_controller/mime_responds.rb23
-rw-r--r--actionpack/lib/action_controller/mime_type.rb8
-rw-r--r--actionpack/lib/action_controller/polymorphic_routes.rb18
-rw-r--r--actionpack/lib/action_controller/rack_process.rb295
-rwxr-xr-xactionpack/lib/action_controller/request.rb535
-rw-r--r--actionpack/lib/action_controller/request_parser.rb315
-rw-r--r--actionpack/lib/action_controller/request_profiler.rb12
-rw-r--r--actionpack/lib/action_controller/rescue.rb12
-rw-r--r--actionpack/lib/action_controller/response.rb179
-rw-r--r--actionpack/lib/action_controller/routing/recognition_optimisation.rb2
-rw-r--r--actionpack/lib/action_controller/routing/route_set.rb67
-rw-r--r--actionpack/lib/action_controller/session/abstract_store.rb166
-rw-r--r--actionpack/lib/action_controller/session/active_record_store.rb350
-rw-r--r--actionpack/lib/action_controller/session/cookie_store.rb377
-rwxr-xr-xactionpack/lib/action_controller/session/drb_server.rb32
-rw-r--r--actionpack/lib/action_controller/session/drb_store.rb35
-rw-r--r--actionpack/lib/action_controller/session/mem_cache_store.rb119
-rw-r--r--actionpack/lib/action_controller/session_management.rb151
-rw-r--r--actionpack/lib/action_controller/streaming.rb15
-rw-r--r--actionpack/lib/action_controller/test_case.rb27
-rw-r--r--actionpack/lib/action_controller/test_process.rb155
-rw-r--r--actionpack/lib/action_controller/uploaded_file.rb37
-rw-r--r--actionpack/lib/action_controller/url_encoded_pair_parser.rb94
-rw-r--r--actionpack/lib/action_controller/verb_piggybacking.rb24
-rw-r--r--actionpack/lib/action_view/base.rb64
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb474
-rw-r--r--actionpack/lib/action_view/helpers/benchmark_helper.rb31
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb77
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb10
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb18
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb11
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/inline_template.rb2
-rw-r--r--actionpack/lib/action_view/locale/en.yml7
-rw-r--r--actionpack/lib/action_view/partials.rb2
-rw-r--r--actionpack/lib/action_view/paths.rb134
-rw-r--r--actionpack/lib/action_view/renderable.rb34
-rw-r--r--actionpack/lib/action_view/renderable_partial.rb7
-rw-r--r--actionpack/lib/action_view/template.rb113
-rw-r--r--actionpack/lib/action_view/test_case.rb28
-rw-r--r--actionpack/test/abstract_unit.rb3
-rw-r--r--actionpack/test/activerecord/active_record_store_test.rb202
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb12
-rw-r--r--actionpack/test/controller/addresses_render_test.rb9
-rw-r--r--actionpack/test/controller/assert_select_test.rb8
-rw-r--r--actionpack/test/controller/base_test.rb22
-rw-r--r--actionpack/test/controller/benchmark_test.rb6
-rw-r--r--actionpack/test/controller/caching_test.rb6
-rw-r--r--actionpack/test/controller/capture_test.rb9
-rw-r--r--actionpack/test/controller/content_type_test.rb9
-rw-r--r--actionpack/test/controller/cookie_test.rb95
-rw-r--r--actionpack/test/controller/dispatcher_test.rb9
-rw-r--r--actionpack/test/controller/filters_test.rb8
-rw-r--r--actionpack/test/controller/flash_test.rb8
-rw-r--r--actionpack/test/controller/http_digest_authentication_test.rb73
-rw-r--r--actionpack/test/controller/integration_test.rb135
-rw-r--r--actionpack/test/controller/integration_upload_test.rb41
-rw-r--r--actionpack/test/controller/layout_test.rb23
-rw-r--r--actionpack/test/controller/middleware_stack_test.rb70
-rw-r--r--actionpack/test/controller/mime_type_test.rb9
-rw-r--r--actionpack/test/controller/rack_test.rb78
-rw-r--r--actionpack/test/controller/render_test.rb113
-rw-r--r--actionpack/test/controller/request/json_params_parsing_test.rb45
-rw-r--r--actionpack/test/controller/request/multipart_params_parsing_test.rb167
-rw-r--r--actionpack/test/controller/request/query_string_parsing_test.rb120
-rw-r--r--actionpack/test/controller/request/xml_params_parsing_test.rb88
-rw-r--r--actionpack/test/controller/request_test.rb321
-rw-r--r--actionpack/test/controller/rescue_test.rb28
-rw-r--r--actionpack/test/controller/routing_test.rb133
-rw-r--r--actionpack/test/controller/send_file_test.rb33
-rw-r--r--actionpack/test/controller/session/cookie_store_test.rb375
-rw-r--r--actionpack/test/controller/session/mem_cache_store_test.rb222
-rw-r--r--actionpack/test/controller/session_fixation_test.rb84
-rw-r--r--actionpack/test/controller/session_management_test.rb178
-rw-r--r--actionpack/test/controller/webservice_test.rb2
-rw-r--r--actionpack/test/fixtures/multipart/hello.txt1
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb22
-rw-r--r--actionpack/test/template/benchmark_helper_test.rb74
-rw-r--r--actionpack/test/template/compiled_templates_test.rb9
-rw-r--r--actionpack/test/template/date_helper_i18n_test.rb11
-rw-r--r--actionpack/test/template/date_helper_test.rb321
-rw-r--r--actionpack/test/template/form_helper_test.rb2
-rw-r--r--actionpack/test/template/render_test.rb7
-rw-r--r--actionpack/test/view/test_case_test.rb8
-rw-r--r--activerecord/CHANGELOG788
-rw-r--r--activerecord/lib/active_record.rb4
-rw-r--r--activerecord/lib/active_record/association_preload.rb53
-rwxr-xr-xactiverecord/lib/active_record/associations.rb97
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb6
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb5
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb6
-rwxr-xr-xactiverecord/lib/active_record/base.rb103
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb52
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/abstract_adapter.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb41
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb4
-rw-r--r--activerecord/lib/active_record/dirty.rb2
-rw-r--r--activerecord/lib/active_record/dynamic_scope_match.rb25
-rw-r--r--activerecord/lib/active_record/query_cache.rb38
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb23
-rw-r--r--activerecord/lib/active_record/session_store.rb319
-rw-r--r--activerecord/lib/active_record/timestamp.rb4
-rw-r--r--activerecord/lib/active_record/transactions.rb4
-rw-r--r--activerecord/lib/active_record/validations.rb7
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb8
-rw-r--r--activerecord/test/cases/associations/eager_test.rb116
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb19
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb7
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb17
-rwxr-xr-xactiverecord/test/cases/base_test.rb15
-rw-r--r--activerecord/test/cases/calculations_test.rb5
-rw-r--r--activerecord/test/cases/helper.rb5
-rw-r--r--activerecord/test/cases/method_scoping_test.rb18
-rw-r--r--activerecord/test/cases/named_scope_test.rb22
-rw-r--r--activerecord/test/cases/reload_models_test.rb4
-rw-r--r--activerecord/test/cases/repair_helper.rb50
-rw-r--r--activerecord/test/cases/validations_i18n_test.rb8
-rw-r--r--activerecord/test/cases/validations_test.rb351
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb7
-rw-r--r--activerecord/test/fixtures/member_types.yml6
-rw-r--r--activerecord/test/fixtures/members.yml4
-rw-r--r--activerecord/test/fixtures/people.yml11
-rw-r--r--activerecord/test/models/company.rb1
-rw-r--r--activerecord/test/models/member.rb1
-rw-r--r--activerecord/test/models/member_detail.rb1
-rw-r--r--activerecord/test/models/member_type.rb3
-rw-r--r--activerecord/test/models/person.rb6
-rw-r--r--activerecord/test/schema/schema.rb11
-rw-r--r--activeresource/CHANGELOG42
-rw-r--r--activeresource/lib/active_resource/connection.rb4
-rw-r--r--activeresource/lib/active_resource/http_mock.rb8
-rw-r--r--activesupport/CHANGELOG189
-rw-r--r--activesupport/lib/active_support/buffered_logger.rb19
-rw-r--r--activesupport/lib/active_support/cache.rb4
-rw-r--r--activesupport/lib/active_support/callbacks.rb41
-rw-r--r--activesupport/lib/active_support/core_ext/benchmark.rb19
-rw-r--r--activesupport/lib/active_support/core_ext/class/attribute_accessors.rb48
-rw-r--r--activesupport/lib/active_support/core_ext/class/delegating_attributes.rb39
-rw-r--r--activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb68
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb20
-rw-r--r--activesupport/lib/active_support/core_ext/hash/slice.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/logger.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/module/aliasing.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors.rb48
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb32
-rw-r--r--activesupport/lib/active_support/core_ext/module/synchronization.rb10
-rw-r--r--activesupport/lib/active_support/core_ext/object/misc.rb15
-rw-r--r--activesupport/lib/active_support/dependencies.rb6
-rw-r--r--activesupport/lib/active_support/deprecation.rb15
-rw-r--r--activesupport/lib/active_support/json/decoding.rb2
-rw-r--r--activesupport/lib/active_support/memoizable.rb58
-rw-r--r--activesupport/lib/active_support/multibyte/unicode_database.rb8
-rw-r--r--activesupport/lib/active_support/ordered_hash.rb85
-rw-r--r--activesupport/lib/active_support/testing/performance.rb2
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb8
-rw-r--r--activesupport/lib/active_support/vendor.rb8
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.1.1/.gitignore3
-rwxr-xr-xactivesupport/lib/active_support/vendor/i18n-0.1.1/MIT-LICENSE20
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.1.1/README.textile20
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.1.1/Rakefile5
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.1.1/i18n.gemspec27
-rwxr-xr-xactivesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n.rb (renamed from activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb)74
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n/backend/simple.rb (renamed from activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb)58
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n/exceptions.rb (renamed from activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/exceptions.rb)6
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.1.1/test/all.rb5
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.1.1/test/i18n_exceptions_test.rb100
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.1.1/test/i18n_test.rb125
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.1.1/test/locale/en.rb1
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.1.1/test/locale/en.yml3
-rw-r--r--activesupport/lib/active_support/vendor/i18n-0.1.1/test/simple_backend_test.rb502
-rw-r--r--activesupport/lib/active_support/vendor/memcache-client-1.5.0.5/memcache.rb (renamed from activesupport/lib/active_support/vendor/memcache-client-1.5.1/memcache.rb)461
-rw-r--r--activesupport/test/buffered_logger_test.rb6
-rw-r--r--activesupport/test/callbacks_test.rb42
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb27
-rw-r--r--activesupport/test/core_ext/module_test.rb27
-rw-r--r--activesupport/test/core_ext/object_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb9
-rw-r--r--activesupport/test/json/decoding_test.rb3
-rw-r--r--activesupport/test/ordered_hash_test.rb69
-rwxr-xr-xci/ci_build.rb15
-rw-r--r--ci/ci_setup_notes.txt11
-rw-r--r--ci/cruise_config.rb3
-rw-r--r--ci/geminstaller.yml8
-rw-r--r--railties/CHANGELOG390
-rw-r--r--railties/doc/guides/html/activerecord_validations_callbacks.html81
-rw-r--r--railties/doc/guides/html/finders.html6
-rw-r--r--railties/doc/guides/html/getting_started_with_rails.html2
-rw-r--r--railties/doc/guides/html/layouts_and_rendering.html2
-rw-r--r--railties/doc/guides/html/testing_rails_applications.html1470
-rw-r--r--railties/doc/guides/source/activerecord_validations_callbacks.txt75
-rw-r--r--railties/doc/guides/source/command_line.txt145
-rw-r--r--railties/doc/guides/source/finders.txt186
-rw-r--r--railties/doc/guides/source/form_helpers.txt121
-rw-r--r--railties/doc/guides/source/getting_started_with_rails.txt2
-rw-r--r--railties/doc/guides/source/i18n.txt260
-rw-r--r--railties/doc/guides/source/images/i18n/demo_localized_pirate.pngbin0 -> 36500 bytes
-rw-r--r--railties/doc/guides/source/images/i18n/demo_translated_en.pngbin0 -> 32877 bytes
-rw-r--r--railties/doc/guides/source/images/i18n/demo_translated_pirate.pngbin0 -> 34506 bytes
-rw-r--r--railties/doc/guides/source/images/i18n/demo_translation_missing.pngbin0 -> 34373 bytes
-rw-r--r--railties/doc/guides/source/images/i18n/demo_untranslated.pngbin0 -> 32793 bytes
-rw-r--r--railties/doc/guides/source/testing_rails_applications.txt20
-rw-r--r--railties/lib/commands/about.rb2
-rw-r--r--railties/lib/commands/dbconsole.rb2
-rw-r--r--railties/lib/commands/runner.rb4
-rw-r--r--railties/lib/commands/server.rb2
-rw-r--r--railties/lib/initializer.rb27
-rw-r--r--railties/lib/rails/backtrace_cleaner.rb8
-rw-r--r--railties/lib/rails/plugin/locator.rb2
-rw-r--r--railties/lib/rails/rack.rb3
-rw-r--r--railties/lib/rails/rack/log_tailer.rb35
-rw-r--r--railties/lib/rails/rack/logger.rb28
-rw-r--r--railties/lib/rails/rack/metal.rb36
-rw-r--r--railties/lib/rails_generator/commands.rb2
-rw-r--r--railties/lib/rails_generator/generators/applications/app/template_runner.rb131
-rw-r--r--railties/lib/rails_generator/generators/components/metal/USAGE8
-rw-r--r--railties/lib/rails_generator/generators/components/metal/metal_generator.rb8
-rw-r--r--railties/lib/rails_generator/generators/components/metal/templates/metal.rb12
-rw-r--r--railties/lib/tasks/databases.rake2
-rw-r--r--railties/lib/tasks/middleware.rake2
-rw-r--r--railties/lib/tasks/tmp.rake4
-rw-r--r--railties/lib/test_help.rb1
-rw-r--r--railties/test/console_app_test.rb10
-rw-r--r--railties/test/error_page_test.rb11
-rw-r--r--railties/test/fcgi_dispatcher_test.rb49
-rw-r--r--railties/test/gem_dependency_test.rb25
-rw-r--r--railties/test/generators/rails_template_runner_test.rb190
260 files changed, 9763 insertions, 7401 deletions
diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG
index 5dc7a33f55..64124e06a8 100644
--- a/actionmailer/CHANGELOG
+++ b/actionmailer/CHANGELOG
@@ -12,7 +12,7 @@
*2.2.0 [RC1] (October 24th, 2008)*
-* Add layout functionality to mailers [Pratik]
+* Add layout functionality to mailers [Pratik Naik]
Mailer layouts behaves just like controller layouts, except layout names need to
have '_mailer' postfix for them to be automatically picked up.
@@ -24,7 +24,7 @@
* Less verbose mail logging: just recipients for :info log level; the whole email for :debug only. #8000 [iaddict, Tarmo Tänav]
-* Updated TMail to version 1.2.1 [raasdnil]
+* Updated TMail to version 1.2.1 [Mikel Lindsaar]
* Fixed that you don't have to call super in ActionMailer::TestCase#setup #10406 [jamesgolick]
@@ -36,7 +36,7 @@
*2.0.1* (December 7th, 2007)
-* Update ActionMailer so it treats ActionView the same way that ActionController does. Closes #10244 [rick]
+* Update ActionMailer so it treats ActionView the same way that ActionController does. Closes #10244 [Rick Olson]
* Pass the template_root as an array as ActionView's view_path
* Request templates with the "#{mailer_name}/#{action}" as opposed to just "#{action}"
@@ -45,11 +45,11 @@
* Update README to use new smtp settings configuration API. Closes #10060 [psq]
-* Allow ActionMailer subclasses to individually set their delivery method (so two subclasses can have different delivery methods) #10033 [zdennis]
+* Allow ActionMailer subclasses to individually set their delivery method (so two subclasses can have different delivery methods) #10033 [Zach Dennis]
-* Update TMail to v1.1.0. Use an updated version of TMail if available. [mikel]
+* Update TMail to v1.1.0. Use an updated version of TMail if available. [Mikel Lindsaar]
-* Introduce a new base test class for testing Mailers. ActionMailer::TestCase [Koz]
+* Introduce a new base test class for testing Mailers. ActionMailer::TestCase [Michael Koziarski]
* Fix silent failure of rxml templates. #9879 [jstewart]
@@ -84,7 +84,7 @@
*1.3.2* (February 5th, 2007)
-* Deprecate server_settings renaming it to smtp_settings, add sendmail_settings to allow you to override the arguments to and location of the sendmail executable. [Koz]
+* Deprecate server_settings renaming it to smtp_settings, add sendmail_settings to allow you to override the arguments to and location of the sendmail executable. [Michael Koziarski]
*1.3.1* (January 16th, 2007)
@@ -104,7 +104,7 @@
* Tighten rescue clauses. #5985 [james@grayproductions.net]
-* Automatically included ActionController::UrlWriter, such that URL generation can happen within ActionMailer controllers. [DHH]
+* Automatically included ActionController::UrlWriter, such that URL generation can happen within ActionMailer controllers. [David Heinemeier Hansson]
* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 730dd2d7aa..c878a8d205 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -17,6 +17,7 @@ module ActionMailer #:nodoc:
# class Notifier < ActionMailer::Base
# def signup_notification(recipient)
# recipients recipient.email_address_with_name
+ # bcc ["bcc@example.com", "Order Watcher <watcher@example.com>"]
# from "system@example.com"
# subject "New account information"
# body :account => recipient
@@ -569,7 +570,9 @@ module ActionMailer #:nodoc:
end
def candidate_for_layout?(options)
- !@template.send(:_exempt_from_layout?, default_template_name)
+ !self.view_paths.find_template(default_template_name, default_template_format).exempt_from_layout?
+ rescue ActionView::MissingTemplate
+ return true
end
def template_root
diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb
index ad1eac912b..a6126d6f7f 100644
--- a/actionmailer/test/abstract_unit.rb
+++ b/actionmailer/test/abstract_unit.rb
@@ -10,11 +10,13 @@ require 'action_mailer/test_case'
ActiveSupport::Deprecation.debug = true
# Bogus template processors
-ActionView::Template.register_template_handler :haml, lambda { |template| "Look its HAML!" }
-ActionView::Template.register_template_handler :bak, lambda { |template| "Lame backup" }
+ActionView::Template.register_template_handler :haml, lambda { |template| "Look its HAML!".inspect }
+ActionView::Template.register_template_handler :bak, lambda { |template| "Lame backup".inspect }
$:.unshift "#{File.dirname(__FILE__)}/fixtures/helpers"
-ActionMailer::Base.template_root = "#{File.dirname(__FILE__)}/fixtures"
+
+FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
+ActionMailer::Base.template_root = FIXTURE_LOAD_PATH
class MockSMTP
def self.deliveries
diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb
index e469302fe1..15a40552c9 100644
--- a/actionmailer/test/mail_service_test.rb
+++ b/actionmailer/test/mail_service_test.rb
@@ -967,13 +967,13 @@ end # uses_mocha
class InheritableTemplateRootTest < Test::Unit::TestCase
def test_attr
expected = "#{File.dirname(__FILE__)}/fixtures/path.with.dots"
- assert_equal expected, FunkyPathMailer.template_root
+ assert_equal expected, FunkyPathMailer.template_root.to_s
sub = Class.new(FunkyPathMailer)
sub.template_root = 'test/path'
- assert_equal 'test/path', sub.template_root
- assert_equal expected, FunkyPathMailer.template_root
+ assert_equal 'test/path', sub.template_root.to_s
+ assert_equal expected, FunkyPathMailer.template_root.to_s
end
end
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 352c4253f4..26e40e76d5 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,22 +1,42 @@
*2.3.0 [Edge]*
+* Added :silence option to BenchmarkHelper#benchmark and turned log_level into a hash parameter and deprecated the old use [DHH]
+
+* Fixed the AssetTagHelper cache to use the computed asset host as part of the cache key instead of just assuming the its a string #1299 [DHH]
+
+* Make ActionController#render(string) work as a shortcut for render :file/:template/:action => string. [#1435] [Pratik Naik] Examples:
+
+ # Instead of render(:action => 'other_action')
+ render('other_action') # argument has no '/'
+ render(:other_action)
+
+ # Instead of render(:template => 'controller/action')
+ render('controller/action') # argument must not begin with a '/', but contain a '/'
+
+ # Instead of render(:file => '/Users/lifo/home.html.erb')
+ render('/Users/lifo/home.html.erb') # argument must begin with a '/'
+
+* Add :prompt option to date/time select helpers. #561 [Sam Oliver]
+
+* Fixed that send_file shouldn't set an etag #1578 [Hongli Lai]
+
* Allow users to opt out of the spoofing checks in Request#remote_ip. Useful for sites whose traffic regularly triggers false positives. [Darren Boyd]
* Deprecated formatted_polymorphic_url. [Jeremy Kemper]
-* Added the option to declare an asset_host as an object that responds to call (see http://github.com/dhh/asset-hosting-with-minimum-ssl for an example) [DHH]
+* Added the option to declare an asset_host as an object that responds to call (see http://github.com/dhh/asset-hosting-with-minimum-ssl for an example) [David Heinemeier Hansson]
-* Added support for multiple routes.rb files (useful for plugin engines). This also means that draw will no longer clear the route set, you have to do that by hand (shouldn't make a difference to you unless you're doing some funky stuff) [DHH]
+* Added support for multiple routes.rb files (useful for plugin engines). This also means that draw will no longer clear the route set, you have to do that by hand (shouldn't make a difference to you unless you're doing some funky stuff) [David Heinemeier Hansson]
* Dropped formatted_* routes in favor of just passing in :format as an option. This cuts resource routes generation in half #1359 [aaronbatalion]
-* Remove support for old double-encoded cookies from the cookie store. These values haven't been generated since before 2.1.0, and any users who have visited the app in the intervening 6 months will have had their cookie upgraded. [Koz]
+* Remove support for old double-encoded cookies from the cookie store. These values haven't been generated since before 2.1.0, and any users who have visited the app in the intervening 6 months will have had their cookie upgraded. [Michael Koziarski]
* Allow helpers directory to be overridden via ActionController::Base.helpers_dir #1424 [Sam Pohlenz]
* Remove deprecated ActionController::Base#assign_default_content_type_and_charset
-* Changed the default of ActionView#render to assume partials instead of files when not given an options hash [DHH]. Examples:
+* Changed the default of ActionView#render to assume partials instead of files when not given an options hash [David Heinemeier Hansson]. Examples:
# Instead of <%= render :partial => "account" %>
<%= render "account" %>
@@ -32,9 +52,9 @@
# <%= render :partial => "posts/post", :collection => @posts %>
<%= render(@posts) %>
-* Remove deprecated render_component. Please use the plugin from http://github.com/rails/render_component/tree/master [Pratik]
+* Remove deprecated render_component. Please use the plugin from http://github.com/rails/render_component/tree/master [Pratik Naik]
-* Fixed RedCloth and BlueCloth shouldn't preload. Instead just assume that they're available if you want to use textilize and markdown and let autoload require them [DHH]
+* Fixed RedCloth and BlueCloth shouldn't preload. Instead just assume that they're available if you want to use textilize and markdown and let autoload require them [David Heinemeier Hansson]
*2.2.2 (November 21st, 2008)*
@@ -51,7 +71,7 @@
product.resources :images, :except => :destroy
end
-* Added render :js for people who want to render inline JavaScript replies without using RJS [DHH]
+* Added render :js for people who want to render inline JavaScript replies without using RJS [David Heinemeier Hansson]
* Fixed that polymorphic_url should compact given array #1317 [hiroshi]
@@ -61,9 +81,9 @@
* Fix regression bug that made date_select and datetime_select raise a Null Pointer Exception when a nil date/datetime was passed and only month and year were displayed #1289 [Bernardo Padua/Tor Erik]
-* Simplified the logging format for parameters (don't include controller, action, and format as duplicates) [DHH]
+* Simplified the logging format for parameters (don't include controller, action, and format as duplicates) [David Heinemeier Hansson]
-* Remove the logging of the Session ID when the session store is CookieStore [DHH]
+* Remove the logging of the Session ID when the session store is CookieStore [David Heinemeier Hansson]
* Fixed regex in redirect_to to fully support URI schemes #1247 [Seth Fitzsimmons]
@@ -74,7 +94,7 @@
* Fix incorrect closing CDATA delimiter and that HTML::Node.parse would blow up on unclosed CDATA sections [packagethief]
-* Added stale? and fresh_when methods to provide a layer of abstraction above request.fresh? and friends [DHH]. Example:
+* Added stale? and fresh_when methods to provide a layer of abstraction above request.fresh? and friends [David Heinemeier Hansson]. Example:
class ArticlesController < ApplicationController
def show_with_respond_to_block
@@ -124,13 +144,13 @@
* Fixed FormTagHelper#submit_tag with :disable_with option wouldn't submit the button's value when was clicked #633 [Jose Fernandez]
-* Stopped logging template compiles as it only clogs up the log [DHH]
+* Stopped logging template compiles as it only clogs up the log [David Heinemeier Hansson]
-* Changed the X-Runtime header to report in milliseconds [DHH]
+* Changed the X-Runtime header to report in milliseconds [David Heinemeier Hansson]
-* Changed BenchmarkHelper#benchmark to report in milliseconds [DHH]
+* Changed BenchmarkHelper#benchmark to report in milliseconds [David Heinemeier Hansson]
-* Changed logging format to be millisecond based and skip misleading stats [DHH]. Went from:
+* Changed logging format to be millisecond based and skip misleading stats [David Heinemeier Hansson]. Went from:
Completed in 0.10000 (4 reqs/sec) | Rendering: 0.04000 (40%) | DB: 0.00400 (4%) | 200 OK [http://example.com]
@@ -154,7 +174,7 @@
* Added button_to_remote helper. #3641 [Donald Piret, Tarmo Tänav]
-* Deprecate render_component. Please use render_component plugin from http://github.com/rails/render_component/tree/master [Pratik]
+* Deprecate render_component. Please use render_component plugin from http://github.com/rails/render_component/tree/master [Pratik Naik]
* Routes may be restricted to lists of HTTP methods instead of a single method or :any. #407 [Brennan Dunn, Gaius Centus Novus]
map.resource :posts, :collection => { :search => [:get, :post] }
@@ -188,7 +208,7 @@
* All 2xx requests are considered successful [Josh Peek]
-* Fixed that AssetTagHelper#compute_public_path shouldn't cache the asset_host along with the source or per-request proc's won't run [DHH]
+* Fixed that AssetTagHelper#compute_public_path shouldn't cache the asset_host along with the source or per-request proc's won't run [David Heinemeier Hansson]
* Removed config.action_view.cache_template_loading, use config.cache_classes instead [Josh Peek]
@@ -251,7 +271,7 @@
* Replaced TemplateFinder abstraction with ViewLoadPaths [Josh Peek]
-* Added block-call style to link_to [Sam Stephenson/DHH]. Example:
+* Added block-call style to link_to [Sam Stephenson/David Heinemeier Hansson]. Example:
<% link_to(@profile) do %>
<strong><%= @profile.name %></strong> -- <span>Check it out!!</span>
@@ -282,30 +302,30 @@
* Added session(:on) to turn session management back on in a controller subclass if the superclass turned it off (Peter Jones) [#136]
-* Change the request forgery protection to go by Content-Type instead of request.format so that you can't bypass it by POSTing to "#{request.uri}.xml" [rick]
+* Change the request forgery protection to go by Content-Type instead of request.format so that you can't bypass it by POSTing to "#{request.uri}.xml" [Rick Olson]
* InstanceTag#default_time_from_options with hash args uses Time.current as default; respects hash settings when time falls in system local spring DST gap [Geoff Buesing]
* select_date defaults to Time.zone.today when config.time_zone is set [Geoff Buesing]
* Fixed that TextHelper#text_field would corrypt when raw HTML was used as the value (mchenryc, Kevin Glowacz) [#80]
-* Added ActionController::TestCase#rescue_action_in_public! to control whether the action under test should use the regular rescue_action path instead of simply raising the exception inline (great for error testing) [DHH]
+* Added ActionController::TestCase#rescue_action_in_public! to control whether the action under test should use the regular rescue_action path instead of simply raising the exception inline (great for error testing) [David Heinemeier Hansson]
-* Reduce number of instance variables being copied from controller to view. [Pratik]
+* Reduce number of instance variables being copied from controller to view. [Pratik Naik]
* select_datetime and select_time default to Time.zone.now when config.time_zone is set [Geoff Buesing]
* datetime_select defaults to Time.zone.now when config.time_zone is set [Geoff Buesing]
-* Remove ActionController::Base#view_controller_internals flag. [Pratik]
+* Remove ActionController::Base#view_controller_internals flag. [Pratik Naik]
* Add conditional options to caches_page method. [Paul Horsfall]
-* Move missing template logic to ActionView. [Pratik]
+* Move missing template logic to ActionView. [Pratik Naik]
-* Introduce ActionView::InlineTemplate class. [Pratik]
+* Introduce ActionView::InlineTemplate class. [Pratik Naik]
-* Automatically parse posted JSON content for Mime::JSON requests. [rick]
+* Automatically parse posted JSON content for Mime::JSON requests. [Rick Olson]
POST /posts
{"post": {"title": "Breaking News"}}
@@ -315,14 +335,14 @@
# ...
end
-* add json_escape ERB util to escape html entities in json strings that are output in HTML pages. [rick]
+* add json_escape ERB util to escape html entities in json strings that are output in HTML pages. [Rick Olson]
* Provide a helper proxy to access helper methods from outside views. Closes #10839 [Josh Peek]
e.g. ApplicationController.helpers.simple_format(text)
* Improve documentation. [Xavier Noria, leethal, jerome]
-* Ensure RJS redirect_to doesn't html-escapes string argument. Closes #8546 [josh, eventualbuddha, Pratik]
+* Ensure RJS redirect_to doesn't html-escapes string argument. Closes #8546 [Josh Peek, eventualbuddha, Pratik Naik]
* Support render :partial => collection of heterogeneous elements. #11491 [Zach Dennis]
@@ -334,17 +354,17 @@
* Fixed HTML::Tokenizer (used in sanitize helper) didn't handle unclosed CDATA tags #10071 [esad, packagethief]
-* Improve documentation. [Radar, Jan De Poorter, chuyeow, xaviershay, danger, miloops, Xavier Noria, Sunny Ripert]
+* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert]
* Fixed that FormHelper#radio_button would produce invalid ids #11298 [harlancrystal]
-* Added :confirm option to submit_tag #11415 [miloops]
+* Added :confirm option to submit_tag #11415 [Emilio Tagua]
* Fixed NumberHelper#number_with_precision to properly round in a way that works equally on Mac, Windows, Linux (closes #11409, #8275, #10090, #8027) [zhangyuanyi]
-* Allow the #simple_format text_helper to take an html_options hash for each paragraph. #2448 [Francois Beausoleil, thechrisoshow]
+* Allow the #simple_format text_helper to take an html_options hash for each paragraph. #2448 [François Beausoleil, Chris O'Sullivan]
-* Fix regression from filter refactoring where re-adding a skipped filter resulted in it being called twice. [rick]
+* Fix regression from filter refactoring where re-adding a skipped filter resulted in it being called twice. [Rick Olson]
* Refactor filters to use Active Support callbacks. #11235 [Josh Peek]
@@ -360,43 +380,43 @@
* Fix nested parameter hash parsing bug. #10797 [thomas.lee]
-* Allow using named routes in ActionController::TestCase before any request has been made. Closes #11273 [alloy]
+* Allow using named routes in ActionController::TestCase before any request has been made. Closes #11273 [Eloy Duran]
-* Fixed that sweepers defined by cache_sweeper will be added regardless of the perform_caching setting. Instead, control whether the sweeper should be run with the perform_caching setting. This makes testing easier when you want to turn perform_caching on/off [DHH]
+* Fixed that sweepers defined by cache_sweeper will be added regardless of the perform_caching setting. Instead, control whether the sweeper should be run with the perform_caching setting. This makes testing easier when you want to turn perform_caching on/off [David Heinemeier Hansson]
* Make MimeResponds::Responder#any work without explicit types. Closes #11140 [jaw6]
* Better error message for type conflicts when parsing params. Closes #7962 [spicycode, matt]
-* Remove unused ActionController::Base.template_class. Closes #10787 [Pratik]
+* Remove unused ActionController::Base.template_class. Closes #10787 [Pratik Naik]
-* Moved template handlers related code from ActionView::Base to ActionView::Template. [Pratik]
+* Moved template handlers related code from ActionView::Base to ActionView::Template. [Pratik Naik]
-* Tests for div_for and content_tag_for helpers. Closes #11223 [thechrisoshow]
+* Tests for div_for and content_tag_for helpers. Closes #11223 [Chris O'Sullivan]
* Allow file uploads in Integration Tests. Closes #11091 [RubyRedRick]
-* Refactor partial rendering into a PartialTemplate class. [Pratik]
+* Refactor partial rendering into a PartialTemplate class. [Pratik Naik]
-* Added that requests with JavaScript as the priority mime type in the accept header and no format extension in the parameters will be treated as though their format was :js when it comes to determining which template to render. This makes it possible for JS requests to automatically render action.js.rjs files without an explicit respond_to block [DHH]
+* Added that requests with JavaScript as the priority mime type in the accept header and no format extension in the parameters will be treated as though their format was :js when it comes to determining which template to render. This makes it possible for JS requests to automatically render action.js.rjs files without an explicit respond_to block [David Heinemeier Hansson]
-* Tests for distance_of_time_in_words with TimeWithZone instances. Closes #10914 [ernesto.jimenez]
+* Tests for distance_of_time_in_words with TimeWithZone instances. Closes #10914 [Ernesto Jimenez]
* Remove support for multivalued (e.g., '&'-delimited) cookies. [Jamis Buck]
* Fix problem with render :partial collections, records, and locals. #11057 [lotswholetime]
-* Added support for naming concrete classes in sweeper declarations [DHH]
+* Added support for naming concrete classes in sweeper declarations [David Heinemeier Hansson]
-* Remove ERB trim variables from trace template in case ActionView::Base.erb_trim_mode is changed in the application. #10098 [tpope, kampers]
+* Remove ERB trim variables from trace template in case ActionView::Base.erb_trim_mode is changed in the application. #10098 [Tim Pope, Chris Kampmeier]
-* Fix typo in form_helper documentation. #10650 [xaviershay, kampers]
+* Fix typo in form_helper documentation. #10650 [Xavier Shay, Chris Kampmeier]
* Fix bug with setting Request#format= after the getter has cached the value. #10889 [cch1]
-* Correct inconsistencies in RequestForgeryProtection docs. #11032 [mislav]
+* Correct inconsistencies in RequestForgeryProtection docs. #11032 [Mislav Marohnić]
-* Introduce a Template class to ActionView. #11024 [lifofifo]
+* Introduce a Template class to ActionView. #11024 [Pratik Naik]
* Introduce the :index option for form_for and fields_for to simplify multi-model forms (see http://railscasts.com/episodes/75). #9883 [rmm5t]
@@ -412,7 +432,7 @@
e.g. map.dashboard '/dashboard', :controller=>'dashboard'
map.root :dashboard
-* Handle corner case with image_tag when passed 'messed up' image names. #9018 [duncanbeevers, mpalmer]
+* Handle corner case with image_tag when passed 'messed up' image names. #9018 [Duncan Beevers, mpalmer]
* Add label_tag helper for generating elements. #10802 [DefV]
@@ -420,15 +440,15 @@
* Performance: optimize route recognition. Large speedup for apps with many resource routes. #10835 [oleganza]
-* Make render :partial recognise form builders and use the _form partial. #10814 [djanowski]
+* Make render :partial recognise form builders and use the _form partial. #10814 [Damian Janowski]
* Allow users to declare other namespaces when using the atom feed helpers. #10304 [david.calavera]
* Introduce send_file :x_sendfile => true to send an X-Sendfile response header. [Jeremy Kemper]
-* Fixed ActionView::Helpers::ActiveRecordHelper::form for when protect_from_forgery is used #10739 [jeremyevans]
+* Fixed ActionView::Helpers::ActiveRecordHelper::form for when protect_from_forgery is used #10739 [Jeremy Evans]
-* Provide nicer access to HTTP Headers. Instead of request.env["HTTP_REFERRER"] you can now use request.headers["Referrer"]. [Koz]
+* Provide nicer access to HTTP Headers. Instead of request.env["HTTP_REFERRER"] you can now use request.headers["Referrer"]. [Michael Koziarski]
* UrlWriter respects relative_url_root. #10748 [Cheah Chu Yeow]
@@ -438,26 +458,26 @@
* assert_response failures include the exception message. #10688 [Seth Rasmussen]
-* All fragment cache keys are now by default prefixed with the "views/" namespace [DHH]
+* All fragment cache keys are now by default prefixed with the "views/" namespace [David Heinemeier Hansson]
-* Moved the caching stores from ActionController::Caching::Fragments::* to ActiveSupport::Cache::*. If you're explicitly referring to a store, like ActionController::Caching::Fragments::MemoryStore, you need to update that reference with ActiveSupport::Cache::MemoryStore [DHH]
+* Moved the caching stores from ActionController::Caching::Fragments::* to ActiveSupport::Cache::*. If you're explicitly referring to a store, like ActionController::Caching::Fragments::MemoryStore, you need to update that reference with ActiveSupport::Cache::MemoryStore [David Heinemeier Hansson]
-* Deprecated ActionController::Base.fragment_cache_store for ActionController::Base.cache_store [DHH]
+* Deprecated ActionController::Base.fragment_cache_store for ActionController::Base.cache_store [David Heinemeier Hansson]
-* Made fragment caching in views work for rjs and builder as well #6642 [zsombor]
+* Made fragment caching in views work for rjs and builder as well #6642 [Dee Zsombor]
* Fixed rendering of partials with layout when done from site layout #9209 [antramm]
-* Fix atom_feed_helper to comply with the atom spec. Closes #10672 [xaviershay]
+* Fix atom_feed_helper to comply with the atom spec. Closes #10672 [Xavier Shay]
* The tags created do not contain a date (http://feedvalidator.org/docs/error/InvalidTAG.html)
* IDs are not guaranteed unique
* A default self link was not provided, contrary to the documentation
* NOTE: This changes tags for existing atom entries, but at least they validate now.
-* Correct indentation in tests. Closes #10671 [l.guidi]
+* Correct indentation in tests. Closes #10671 [Luca Guidi]
-* Fix that auto_link looks for ='s in url paths (Amazon urls have them). Closes #10640 [bgreenlee]
+* Fix that auto_link looks for ='s in url paths (Amazon urls have them). Closes #10640 [Brad Greenlee]
* Ensure that test case setup is run even if overridden. #10382 [Josh Peek]
@@ -474,7 +494,7 @@
* Added OPTIONS to list of default accepted HTTP methods #10449 [holoway]
-* Added option to pass proc to ActionController::Base.asset_host for maximum configurability #10521 [chuyeow]. Example:
+* Added option to pass proc to ActionController::Base.asset_host for maximum configurability #10521 [Cheah Chu Yeow]. Example:
ActionController::Base.asset_host = Proc.new { |source|
if source.starts_with?('/images')
@@ -503,45 +523,45 @@
* Fixed send_file/binary_content for testing #8044 [tolsen]
-* When a NonInferrableControllerError is raised, make the proposed fix clearer in the error message. Closes #10199 [danger]
+* When a NonInferrableControllerError is raised, make the proposed fix clearer in the error message. Closes #10199 [Jack Danger Canty]
* Update Prototype to 1.6.0.1. [sam]
* Update script.aculo.us to 1.8.0.1. [madrobby]
-* Add 'disabled' attribute to <OPTION> separators used in time zone and country selects. Closes #10354 [hasmanyjosh]
+* Add 'disabled' attribute to <OPTION> separators used in time zone and country selects. Closes #10354 [Josh Susser]
-* Added the same record identification guessing rules to fields_for as form_for has [DHH]
+* Added the same record identification guessing rules to fields_for as form_for has [David Heinemeier Hansson]
-* Fixed that verification violations with no specified action didn't halt the chain (now they do with a 400 Bad Request) [DHH]
+* Fixed that verification violations with no specified action didn't halt the chain (now they do with a 400 Bad Request) [David Heinemeier Hansson]
* Raise UnknownHttpMethod exception for unknown HTTP methods. Closes #10303 [Tarmo Tänav]
* Update to Prototype -r8232. [sam]
-* Make sure the optimisation code for routes doesn't get used if :host, :anchor or :port are provided in the hash arguments. [pager, Koz] #10292
+* Make sure the optimisation code for routes doesn't get used if :host, :anchor or :port are provided in the hash arguments. [pager, Michael Koziarski] #10292
* Added protection from trailing slashes on page caching #10229 [devrieda]
-* Asset timestamps are appended, not prepended. Closes #10276 [mnaberez]
+* Asset timestamps are appended, not prepended. Closes #10276 [Mike Naberezny]
* Minor inconsistency in description of render example. Closes #10029 [ScottSchram]
-* Add #prepend_view_path and #append_view_path instance methods on ActionController::Base for consistency with the class methods. [rick]
+* Add #prepend_view_path and #append_view_path instance methods on ActionController::Base for consistency with the class methods. [Rick Olson]
-* Refactor sanitizer helpers into HTML classes and make it easy to swap them out with custom implementations. Closes #10129. [rick]
+* Refactor sanitizer helpers into HTML classes and make it easy to swap them out with custom implementations. Closes #10129. [Rick Olson]
-* Add deprecation for old subtemplate syntax for ActionMailer templates, use render :partial [rick]
+* Add deprecation for old subtemplate syntax for ActionMailer templates, use render :partial [Rick Olson]
-* Fix TemplateError so it doesn't bomb on exceptions while running tests [rick]
+* Fix TemplateError so it doesn't bomb on exceptions while running tests [Rick Olson]
-* Fixed that named routes living under resources shouldn't have double slashes #10198 [isaacfeliu]
+* Fixed that named routes living under resources shouldn't have double slashes #10198 [Isaac Feliu]
-* Make sure that cookie sessions use a secret that is at least 30 chars in length. [Koz]
+* Make sure that cookie sessions use a secret that is at least 30 chars in length. [Michael Koziarski]
* Fixed that partial rendering should look at the type of the first render to determine its own type if no other clues are available (like when using text.plain.erb as the extension in AM) #10130 [java]
-* Fixed that has_many :through associations should render as collections too #9051 [mathie/danger]
+* Fixed that has_many :through associations should render as collections too #9051 [mathie/Jack Danger Canty]
* Added :mouseover short-cut to AssetTagHelper#image_tag for doing easy image swaps #6893 [joost]
@@ -549,7 +569,7 @@
* Fix syntax error in documentation example for cycle method. Closes #8735 [foca]
-* Document :with option for link_to_remote. Closes #8765 [ryanb]
+* Document :with option for link_to_remote. Closes #8765 [Ryan Bates]
* Document :minute_step option for time_select. Closes #8814 [brupm]
@@ -559,7 +579,7 @@
* Fix broken tag in assert_tag documentation. Closes #9037 [mfazekas]
-* Add documentation for route conditions. Closes #9041 [innu, manfred]
+* Add documentation for route conditions. Closes #9041 [innu, Manfred Stienstra]
* Fix typo left over from previous typo fix in url helper. Closes #9414 [Henrik N]
@@ -567,23 +587,23 @@
* Update Prototype to 1.6.0 and script.aculo.us to 1.8.0. [sam, madrobby]
-* Expose the cookie jar as a helper method (before the view would just get the raw cookie hash) [DHH]
+* Expose the cookie jar as a helper method (before the view would just get the raw cookie hash) [David Heinemeier Hansson]
* Integration tests: get_ and post_via_redirect take a headers hash. #9130 [simonjefford]
-* Simplfy #view_paths implementation. ActionView templates get the exact object, not a dup. [Rick]
+* Simplfy #view_paths implementation. ActionView templates get the exact object, not a dup. [Rick Olson]
-* Update tests for ActiveSupport's JSON escaping change. [rick]
+* Update tests for ActiveSupport's JSON escaping change. [Rick Olson]
* FormHelper's auto_index should use #to_param instead of #id_before_type_cast. Closes #9994 [mattly]
* Doc typo fixes for ActiveRecordHelper. Closes #9973 [mikong]
-* Make example parameters in restful routing docs idiomatic. Closes #9993 [danger]
+* Make example parameters in restful routing docs idiomatic. Closes #9993 [Jack Danger Canty]
* Make documentation comment for mime responders match documentation example. Closes #9357 [yon]
-* Introduce a new test case class for functional tests. ActionController::TestCase. [Koz]
+* Introduce a new test case class for functional tests. ActionController::TestCase. [Michael Koziarski]
* Fix incorrect path in helper rdoc. Closes #9926 [viktor tron]
@@ -599,13 +619,13 @@
* Disabled checkboxes don't submit a form value. #9301 [vladr, robinjfisher]
-* Added tests for options to ActiveRecordHelper#form. Closes #7213 [richcollins, mikong, mislav]
+* Added tests for options to ActiveRecordHelper#form. Closes #7213 [richcollins, mikong, Mislav Marohnić]
-* Changed before_filter halting to happen automatically on render or redirect but no longer on simply returning false [DHH]
+* Changed before_filter halting to happen automatically on render or redirect but no longer on simply returning false [David Heinemeier Hansson]
* Ensure that cookies handle array values correctly. Closes #9937 [queso]
-* Make sure resource routes don't clash with internal helpers like javascript_path, image_path etc. #9928 [gbuesing]
+* Make sure resource routes don't clash with internal helpers like javascript_path, image_path etc. #9928 [Geoff Buesing]
* caches_page uses a single after_filter instead of one per action. #9891 [Pratik Naik]
@@ -617,15 +637,15 @@
* error_messages_for also takes :message and :header_message options which defaults to the old "There were problems with the following fields:" and "<count> errors prohibited this <object_name> from being saved". #8270 [rmm5t, zach-inglis-lt3]
-* Make sure that custom inflections are picked up by map.resources. #9815 [mislav]
+* Make sure that custom inflections are picked up by map.resources. #9815 [Mislav Marohnić]
-* Changed SanitizeHelper#sanitize to only allow the custom attributes and tags when specified in the call [DHH]
+* Changed SanitizeHelper#sanitize to only allow the custom attributes and tags when specified in the call [David Heinemeier Hansson]
-* Extracted sanitization methods from TextHelper to SanitizeHelper [DHH]
+* Extracted sanitization methods from TextHelper to SanitizeHelper [David Heinemeier Hansson]
* rescue_from accepts :with => lambda { |exception| ... } or a normal block. #9827 [Pratik Naik]
-* Add :status to redirect_to allowing users to choose their own response code without manually setting headers. #8297 [codahale, chasgrundy]
+* Add :status to redirect_to allowing users to choose their own response code without manually setting headers. #8297 [Coda Hale, chasgrundy]
* Add link_to :back which uses your referrer with a fallback to a javascript link. #7366 [eventualbuddha, Tarmo Tänav]
@@ -633,13 +653,13 @@
* Fix url_for, redirect_to, etc. with :controller => :symbol instead of 'string'. #8562, #9525 [Justin Lynn, Tarmo Tänav, shoe]
-* Use #require_library_or_gem to load the memcache library for the MemCache session and fragment cache stores. Closes #8662. [Rick]
+* Use #require_library_or_gem to load the memcache library for the MemCache session and fragment cache stores. Closes #8662. [Rick Olson]
-* Move ActionController::Routing.optimise_named_routes to ActionController::Base.optimise_named_routes. Now you can set it in the config. [Rick]
+* Move ActionController::Routing.optimise_named_routes to ActionController::Base.optimise_named_routes. Now you can set it in the config. [Rick Olson]
config.action_controller.optimise_named_routes = false
-* ActionController::Routing::DynamicSegment#interpolation_chunk should call #to_s on all values before calling URI.escape. [Rick]
+* ActionController::Routing::DynamicSegment#interpolation_chunk should call #to_s on all values before calling URI.escape. [Rick Olson]
* Only accept session ids from cookies, prevents session fixation attacks. [bradediger]
@@ -648,7 +668,7 @@
* Fixed that render template did not honor exempt_from_layout #9698 [pezra]
-* Better error messages if you leave out the :secret option for request forgery protection. Closes #9670 [rick]
+* Better error messages if you leave out the :secret option for request forgery protection. Closes #9670 [Rick Olson]
* Allow ability to disable request forgery protection, disable it in test mode by default. Closes #9693 [Pratik Naik]
@@ -660,40 +680,40 @@
* Cache asset ids. [Jeremy Kemper]
-* Optimized named routes respect AbstractRequest.relative_url_root. #9612 [danielmorrison, Jeremy Kemper]
+* Optimized named routes respect AbstractRequest.relative_url_root. #9612 [Daniel Morrison, Jeremy Kemper]
* Introduce ActionController::Base.rescue_from to declare exception-handling methods. Cleaner style than the case-heavy rescue_action_in_public. #9449 [Norbert Crombach]
-* Rename some RequestForgeryProtection methods. The class method is now #protect_from_forgery, and the default parameter is now 'authenticity_token'. [Rick]
+* Rename some RequestForgeryProtection methods. The class method is now #protect_from_forgery, and the default parameter is now 'authenticity_token'. [Rick Olson]
-* Merge csrf_killer plugin into rails. Adds RequestForgeryProtection model that verifies session-specific _tokens for non-GET requests. [Rick]
+* Merge csrf_killer plugin into rails. Adds RequestForgeryProtection model that verifies session-specific _tokens for non-GET requests. [Rick Olson]
-* Secure #sanitize, #strip_tags, and #strip_links helpers against xss attacks. Closes #8877. [Rick, Pratik Naik, Jacques Distler]
+* Secure #sanitize, #strip_tags, and #strip_links helpers against xss attacks. Closes #8877. [Rick Olson, Pratik Naik, Jacques Distler]
This merges and renames the popular white_list helper (along with some css sanitizing from Jacques Distler version of the same plugin).
Also applied updated versions of #strip_tags and #strip_links from #8877.
* Remove use of & logic operator. Closes #8114. [watson]
-* Fixed JavaScriptHelper#escape_javascript to also escape closing tags #8023 [rubyruy]
+* Fixed JavaScriptHelper#escape_javascript to also escape closing tags #8023 [Ruy Asan]
* Fixed TextHelper#word_wrap for multiline strings with extra carrier returns #8663 [seth]
* Fixed that setting the :host option in url_for would automatically turn off :only_path (since :host would otherwise not be shown) #9586 [Bounga]
-* Added FormHelper#label. #8641, #9850 [jcoglan, jarkko]
+* Added FormHelper#label. #8641, #9850 [jcoglan, Jarkko Laine]
-* Added AtomFeedHelper (slightly improved from the atom_feed_helper plugin) [DHH]
+* Added AtomFeedHelper (slightly improved from the atom_feed_helper plugin) [David Heinemeier Hansson]
-* Prevent errors when generating routes for uncountable resources, (i.e. sheep where plural == singluar). map.resources :sheep now creates sheep_index_url for the collection and sheep_url for the specific item. [Koz]
+* Prevent errors when generating routes for uncountable resources, (i.e. sheep where plural == singluar). map.resources :sheep now creates sheep_index_url for the collection and sheep_url for the specific item. [Michael Koziarski]
-* Added support for HTTP Only cookies (works in IE6+ and FF 2.0.5+) as an improvement for XSS attacks #8895 [Pratik Naik, Spakman]
+* Added support for HTTP Only cookies (works in IE6+ and FF 2.0.5+) as an improvement for XSS attacks #8895 [Pratik Naik, Mark Somerville]
* Don't warn when a path segment precedes a required segment. Closes #9615. [Nicholas Seckar]
* Fixed CaptureHelper#content_for to work with the optional content parameter instead of just the block #9434 [sandofsky/wildchild].
-* Added Mime::Type.register_alias for dealing with different formats using the same mime type [DHH]. Example:
+* Added Mime::Type.register_alias for dealing with different formats using the same mime type [David Heinemeier Hansson]. Example:
class PostsController < ApplicationController
before_filter :adjust_format_for_iphone
@@ -716,7 +736,7 @@
end
end
-* Added that render :json will automatically call .to_json unless it's being passed a string [DHH].
+* Added that render :json will automatically call .to_json unless it's being passed a string [David Heinemeier Hansson].
* Autolink behaves well with emails embedded in URLs. #7313 [Jeremy McAnally, Tarmo Tänav]
@@ -728,7 +748,7 @@
* root_path returns '/' not ''. #9563 [Pratik Naik]
-* Fixed that setting request.format should also affect respond_to blocks [DHH]
+* Fixed that setting request.format should also affect respond_to blocks [David Heinemeier Hansson]
* Add option to force binary mode on tempfile used for fixture_file_upload. #6380 [Jonathan Viney]
@@ -738,20 +758,20 @@
* Moved ActionController::Macros::InPlaceEditing into the in_place_editor plugin on the official Rails svn. #9513 [Pratik Naik]
-* Removed deprecated form of calling xml_http_request/xhr without the first argument being the http verb [DHH]
+* Removed deprecated form of calling xml_http_request/xhr without the first argument being the http verb [David Heinemeier Hansson]
-* Removed deprecated methods [DHH]:
+* Removed deprecated methods [David Heinemeier Hansson]:
- ActionController::Base#keep_flash (use flash.keep instead)
- ActionController::Base#expire_matched_fragments (just call expire_fragment with a regular expression)
- ActionController::Base.template_root/= methods (use ActionController#Base.view_paths/= instead)
- ActionController::Base.cookie (use ActionController#Base.cookies[]= instead)
-* Removed the deprecated behavior of appending ".png" to image_tag/image_path calls without an existing extension [DHH]
+* Removed the deprecated behavior of appending ".png" to image_tag/image_path calls without an existing extension [David Heinemeier Hansson]
-* Removed ActionController::Base.scaffold -- it went through the whole idea of scaffolding (card board walls you remove and tweak one by one). Use the scaffold generator instead (it does resources too now!) [DHH]
+* Removed ActionController::Base.scaffold -- it went through the whole idea of scaffolding (card board walls you remove and tweak one by one). Use the scaffold generator instead (it does resources too now!) [David Heinemeier Hansson]
-* Optimise named route generation when using positional arguments. [Koz]
+* Optimise named route generation when using positional arguments. [Michael Koziarski]
This change delivers significant performance benefits for the most
common usage scenarios for modern rails applications by avoiding the
@@ -762,9 +782,9 @@
* Fix layout overriding response status. #9476 [lotswholetime]
-* Add field_set_tag for generating field_sets, closes #9477. [djanowski]
+* Add field_set_tag for generating field_sets, closes #9477. [Damian Janowski]
-* Allow additional parameters to be passed to named route helpers when using positional arguments. Closes #8930 [ian.w.white@gmail.com]
+* Allow additional parameters to be passed to named route helpers when using positional arguments. Closes #8930 [Ian White]
* Make render :partial work with a :collection of Hashes, previously this wasn't possible due to backwards compatibility restrictions. [Pratik Naik]
@@ -774,15 +794,15 @@
* Find layouts even if they're not in the first view_paths directory. Closes #9258 [caio]
-* Major improvement to the documentation for the options / select form helpers. Closes #9038 [kampers, jardeon, wesg]
+* Major improvement to the documentation for the options / select form helpers. Closes #9038 [Chris Kampmeier, jardeon, wesg]
* Fix number_to_human_size when using different precisions. Closes #7536. [RichardStrand, mpalmer]
-* Added partial layouts (see example in action_view/lib/partials.rb) [DHH]
+* Added partial layouts (see example in action_view/lib/partials.rb) [David Heinemeier Hansson]
-* Allow you to set custom :conditions on resource routes. [Rick]
+* Allow you to set custom :conditions on resource routes. [Rick Olson]
-* Fixed that file.content_type for uploaded files would include a trailing \r #9053 [bgreenlee]
+* Fixed that file.content_type for uploaded files would include a trailing \r #9053 [Brad Greenlee]
* url_for now accepts a series of symbols representing the namespace of the record [Josh Knowles]
@@ -790,7 +810,7 @@
* Make sure missing template exceptions actually say which template they were looking for. Closes #8683 [dasil003]
-* Fix errors with around_filters which do not yield, restore 1.1 behaviour with after filters. Closes #8891 [skaes]
+* Fix errors with around_filters which do not yield, restore 1.1 behaviour with after filters. Closes #8891 [Stefan Kaes]
After filters will *no longer* be run if an around_filter fails to yield, users relying on
this behaviour are advised to put the code in question after a yield statement in an around filter.
@@ -802,13 +822,13 @@
render :partial => 'show.html.erb'
-* Improve capture helper documentation. #8796 [kampers]
+* Improve capture helper documentation. #8796 [Chris Kampmeier]
* Prefix nested resource named routes with their action name, e.g. new_group_user_path(@group) instead of group_new_user_path(@group). The old nested action named route is deprecated in Rails 1.2.4. #8558 [David Chelimsky]
-* Allow sweepers to be created solely for expiring after controller actions, not model changes [DHH]
+* Allow sweepers to be created solely for expiring after controller actions, not model changes [David Heinemeier Hansson]
-* Added assigns method to ActionController::Caching::Sweeper to easily access instance variables on the controller [DHH]
+* Added assigns method to ActionController::Caching::Sweeper to easily access instance variables on the controller [David Heinemeier Hansson]
* Give the legacy X-POST_DATA_FORMAT header greater precedence during params parsing for backward compatibility. [Jeremy Kemper]
@@ -816,11 +836,11 @@
* Fixed that radio_button_tag should generate unique ids #3353 [Bob Silva, Rebecca, Josh Peek]
-* Fixed that HTTP authentication should work if the header is called REDIRECT_X_HTTP_AUTHORIZATION as well #6754 [mislaw]
+* Fixed that HTTP authentication should work if the header is called REDIRECT_X_HTTP_AUTHORIZATION as well #6754 [Mislav Marohnić]
* Don't mistakenly interpret the request uri as the query string. #8731 [Pratik Naik, Jeremy Kemper]
-* Make ActionView#view_paths an attr_accessor for real this time. Also, don't perform an unnecessary #compact on the @view_paths array in #initialize. Closes #8582 [dasil003, julik, rick]
+* Make ActionView#view_paths an attr_accessor for real this time. Also, don't perform an unnecessary #compact on the @view_paths array in #initialize. Closes #8582 [dasil003, julik, Rick Olson]
* Tolerate missing content type on multipart file uploads. Fix for Safari 3. [Jeremy Kemper]
@@ -836,7 +856,7 @@
* Resources: url_for([parent, child]) generates /parents/1/children/2 for the nested resource. Likewise with the other simply helpful methods like form_for and link_to. #6432 [mhw, Jonathan Vaught, lotswholetime]
-* Assume html format when rendering partials in RJS. #8076 [Rick]
+* Assume html format when rendering partials in RJS. #8076 [Rick Olson]
* Don't double-escape url_for in views. #8144 [Rich Collins, Josh Peek]
@@ -856,13 +876,13 @@
* Reduce file stat calls when checking for template changes. #7736 [alex]
-* Added custom path cache_page/expire_page parameters in addition to the options hashes [DHH]. Example:
+* Added custom path cache_page/expire_page parameters in addition to the options hashes [David Heinemeier Hansson]. Example:
def index
caches_page(response.body, "/index.html")
end
-* Action Caching speedup. #8231 [skaes]
+* Action Caching speedup. #8231 [Stefan Kaes]
* Wordsmith resources documentation. #8484 [marclove]
@@ -880,21 +900,21 @@
* Added option to suppress :size when using :maxlength for FormTagHelper#text_field #3112 [Tim Pope]
-* catch possible WSOD when trying to render a missing partial. Closes #8454 [Catfish]
+* catch possible WSOD when trying to render a missing partial. Closes #8454 [Jonathan del Strother]
* Rewind request body after reading it, if possible. #8438 [s450r1]
-* Resource namespaces are inherited by their has_many subresources. #8280 [marclove, ggarside]
+* Resource namespaces are inherited by their has_many subresources. #8280 [marclove, Geoff Garside]
* Fix filtered parameter logging with nil parameter values. #8422 [choonkeat]
-* Integration tests: alias xhr to xml_http_request and add a request_method argument instead of always using POST. #7124 [Nik Wakelin, Francois Beausoleil, Wizard]
+* Integration tests: alias xhr to xml_http_request and add a request_method argument instead of always using POST. #7124 [Nik Wakelin, François Beausoleil, Wizard]
* Document caches_action. #5419 [Jarkko Laine]
* Update to Prototype 1.5.1. [Sam Stephenson]
-* Allow routes to be decalred under namespaces [Tobias Luetke]:
+* Allow routes to be decalred under namespaces [Tobias Lütke]:
map.namespace :admin do |admin|
admin.root :controller => "products"
@@ -903,13 +923,13 @@
* Update to script.aculo.us 1.7.1_beta3. [Thomas Fuchs]
-* observe_form always sends the serialized form. #5271 [manfred, normelton@gmail.com]
+* observe_form always sends the serialized form. #5271 [Manfred Stienstra, normelton@gmail.com]
* Parse url-encoded and multipart requests ourselves instead of delegating to CGI. [Jeremy Kemper]
* select :include_blank option can be set to a string instead of true, which just uses an empty string. #7664 [Wizard]
-* Added url_for usage on render :location, which allows for record identification [DHH]. Example:
+* Added url_for usage on render :location, which allows for record identification [David Heinemeier Hansson]. Example:
render :xml => person, :status => :created, :location => person
@@ -935,7 +955,7 @@
end
end
-* Added record identifications to FormHelper#form_for and PrototypeHelper#remote_form_for [DHH]. Examples:
+* Added record identifications to FormHelper#form_for and PrototypeHelper#remote_form_for [David Heinemeier Hansson]. Examples:
<% form_for(@post) do |f| %>
...
@@ -959,28 +979,28 @@
...
<% end %>
-* Rationalize route path escaping according to RFC 2396 section 3.3. #7544, #8307. [Jeremy Kemper, chrisroos, begemot, jugend]
+* Rationalize route path escaping according to RFC 2396 section 3.3. #7544, #8307. [Jeremy Kemper, Chris Roos, begemot, jugend]
-* Added record identification with polymorphic routes for ActionController::Base#url_for and ActionView::Base#url_for [DHH]. Examples:
+* Added record identification with polymorphic routes for ActionController::Base#url_for and ActionView::Base#url_for [David Heinemeier Hansson]. Examples:
redirect_to(post) # => redirect_to(posts_url(post)) => Location: http://example.com/posts/1
link_to(post.title, post) # => link_to(post.title, posts_url(post)) => <a href="/posts/1">Hello world</a>
Any method that calls url_for on its parameters will automatically benefit from this.
-* Removed deprecated parameters_for_method_reference concept (legacy from before named routes) [DHH]
+* Removed deprecated parameters_for_method_reference concept (legacy from before named routes) [David Heinemeier Hansson]
* Add ActionController::Routing::Helpers, a module to contain common URL helpers such as polymorphic_url. [Nicholas Seckar]
-* Included the HttpAuthentication plugin as part of core (ActionController::HttpAuthentication::Basic) [DHH]
+* Included the HttpAuthentication plugin as part of core (ActionController::HttpAuthentication::Basic) [David Heinemeier Hansson]
-* Modernize documentation for form helpers. [jeremymcanally]
+* Modernize documentation for form helpers. [Jeremy McAnally]
* Add brief introduction to REST to the resources documentation. [fearoffish]
* Fix various documentation typos throughout ActionPack. [Henrik N]
-* Enhance documentation and add examples for url_for. [jeremymcanally]
+* Enhance documentation and add examples for url_for. [Jeremy McAnally]
* Fix documentation typo in routes. [Norbert Crombach, pam]
@@ -990,24 +1010,24 @@
* Replace the current block/continuation filter chain handling by an implementation based on a simple loop. #8226 [Stefan Kaes]
-* Update UrlWriter to accept :anchor parameter. Closes #6771. [octopod]
+* Update UrlWriter to accept :anchor parameter. Closes #6771. [Chris McGrath]
-* Added RecordTagHelper for using RecordIdentifier conventions on divs and other container elements [DHH]. Example:
+* Added RecordTagHelper for using RecordIdentifier conventions on divs and other container elements [David Heinemeier Hansson]. Example:
<% div_for(post) do %> <div id="post_45" class="post">
<%= post.body %> What a wonderful world!
<% end %> </div>
-* Added page[record] accessor to JavaScriptGenerator that relies on RecordIdentifier to find the right dom id [DHH]. Example:
+* Added page[record] accessor to JavaScriptGenerator that relies on RecordIdentifier to find the right dom id [David Heinemeier Hansson]. Example:
format.js do
# Calls: new Effect.fade('post_45');
render(:update) { |page| page[post].visual_effect(:fade) }
end
-* Added RecordIdentifier to enforce view conventions on records for dom ids, classes, and partial paths [DHH]
+* Added RecordIdentifier to enforce view conventions on records for dom ids, classes, and partial paths [David Heinemeier Hansson]
-* Added map.namespace to deal with the common situation of admin sections and the like [DHH]
+* Added map.namespace to deal with the common situation of admin sections and the like [David Heinemeier Hansson]
Before:
@@ -1025,7 +1045,7 @@
:has_many => [ :tags, :images, :variants ]
end
-* Added :name_prefix as standard for nested resources [DHH]. WARNING: May be backwards incompatible with your app
+* Added :name_prefix as standard for nested resources [David Heinemeier Hansson]. WARNING: May be backwards incompatible with your app
Before:
@@ -1043,7 +1063,7 @@
This does mean that if you intended to have comments_url go to /emails/5/comments, then you'll have to set :name_prefix to nil explicitly.
-* Added :has_many and :has_one for declaring plural and singular resources beneath the current [DHH]
+* Added :has_many and :has_one for declaring plural and singular resources beneath the current [David Heinemeier Hansson]
Before:
@@ -1057,28 +1077,28 @@
map.resources :notes, :has_many => [ :comments, :attachments ], :has_one => :author
-* Added that render :xml will try to call to_xml if it can [DHH]. Makes these work:
+* Added that render :xml will try to call to_xml if it can [David Heinemeier Hansson]. Makes these work:
render :xml => post
render :xml => comments
-* Added :location option to render so that the common pattern of rendering a response after creating a new resource is now a 1-liner [DHH]
+* Added :location option to render so that the common pattern of rendering a response after creating a new resource is now a 1-liner [David Heinemeier Hansson]
render :xml => post.to_xml, :status => :created, :location => post_url(post)
-* Ensure that render_text only adds string content to the body of the response [DHH]
+* Ensure that render_text only adds string content to the body of the response [David Heinemeier Hansson]
* Return the string representation from an Xml Builder when rendering a partial. Closes #5044 [Tim Pope]
-* Fixed that parameters from XML should also be presented in a hash with indifferent access [DHH]
+* Fixed that parameters from XML should also be presented in a hash with indifferent access [David Heinemeier Hansson]
-* Tweak template format rules so that the ACCEPT header is only used if it's text/javascript. This is so ajax actions without a :format param get recognized as Mime::JS. [Rick]
+* Tweak template format rules so that the ACCEPT header is only used if it's text/javascript. This is so ajax actions without a :format param get recognized as Mime::JS. [Rick Olson]
-* The default respond_to blocks don't set a specific extension anymore, so that both 'show.rjs' and 'show.js.rjs' will work. [Rick]
+* The default respond_to blocks don't set a specific extension anymore, so that both 'show.rjs' and 'show.js.rjs' will work. [Rick Olson]
* Allow layouts with extension of .html.erb. Closes #8032 [Josh Knowles]
-* Change default respond_to templates for xml and rjs formats. [Rick]
+* Change default respond_to templates for xml and rjs formats. [Rick Olson]
* Default xml template goes from #{action_name}.rxml => #{action_name}.xml.builder.
* Default rjs template goes from #{action_name}.rjs => #{action_name}.js.rjs.
@@ -1091,21 +1111,21 @@
end
end
-* Fix WSOD due to modification of a formatted template extension so that requests to templates like 'foo.html.erb' fail on the second hit. [Rick]
+* Fix WSOD due to modification of a formatted template extension so that requests to templates like 'foo.html.erb' fail on the second hit. [Rick Olson]
-* Fix WSOD when template compilation fails [Rick]
+* Fix WSOD when template compilation fails [Rick Olson]
-* Change ActionView template defaults. Look for templates using the request format first, such as "show.html.erb" or "show.xml.builder", before looking for the old defaults like "show.erb" or "show.builder" [Rick]
+* Change ActionView template defaults. Look for templates using the request format first, such as "show.html.erb" or "show.xml.builder", before looking for the old defaults like "show.erb" or "show.builder" [Rick Olson]
* Highlight helper highlights one or many terms in a single pass. [Jeremy Kemper]
-* Dropped the use of ; as a separator of non-crud actions on resources and went back to the vanilla slash. It was a neat idea, but lots of the non-crud actions turned out not to be RPC (as the ; was primarily intended to discourage), but legitimate sub-resources, like /parties/recent, which didn't deserve the uglification of /parties;recent. Further more, the semicolon caused issues with caching and HTTP authentication in Safari. Just Not Worth It [DHH]
+* Dropped the use of ; as a separator of non-crud actions on resources and went back to the vanilla slash. It was a neat idea, but lots of the non-crud actions turned out not to be RPC (as the ; was primarily intended to discourage), but legitimate sub-resources, like /parties/recent, which didn't deserve the uglification of /parties;recent. Further more, the semicolon caused issues with caching and HTTP authentication in Safari. Just Not Worth It [David Heinemeier Hansson]
-* Added that FormTagHelper#submit_tag will return to its original state if the submit fails and you're using :disable_with [DHH]
+* Added that FormTagHelper#submit_tag will return to its original state if the submit fails and you're using :disable_with [David Heinemeier Hansson]
-* Cleaned up, corrected, and mildly expanded ActionPack documentation. Closes #7190 [jeremymcanally]
+* Cleaned up, corrected, and mildly expanded ActionPack documentation. Closes #7190 [Jeremy McAnally]
-* Small collection of ActionController documentation cleanups. Closes #7319 [jeremymcanally]
+* Small collection of ActionController documentation cleanups. Closes #7319 [Jeremy McAnally]
* Make sure the route expiry hash is constructed by comparing the to_param-ized values of each hash. [Jamis Buck]
@@ -1135,19 +1155,19 @@
* Allow array and hash query parameters. Array route parameters are converted/to/a/path as before. #6765, #7047, #7462 [bgipsy, Jeremy McAnally, Dan Kubb, brendan]
# Add a #dbman attr_reader for CGI::Session and make CGI::Session::CookieStore#generate_digest public so it's easy to generate digests
-using the cookie store's secret. [Rick]
+using the cookie store's secret. [Rick Olson]
-* Added Request#url that returns the complete URL used for the request [DHH]
+* Added Request#url that returns the complete URL used for the request [David Heinemeier Hansson]
* Extract dynamic scaffolding into a plugin. #7700 [Josh Peek]
-* Added user/password options for url_for to add http authentication in a URL [DHH]
+* Added user/password options for url_for to add http authentication in a URL [David Heinemeier Hansson]
* Fixed that FormTagHelper#text_area_tag should disregard :size option if it's not a string [Brendon Davidson]
* Set the original button value in an attribute of the button when using the :disable_with key with submit_tag, so that the original can be restored later. [Jamis Buck]
-* session_enabled? works with session :off. #6680 [Catfish]
+* session_enabled? works with session :off. #6680 [Jonathan del Strother]
* Added :port and :host handling to UrlRewriter (which unified url_for usage, regardless of whether it's called in view or controller) #7616 [alancfrancis]
@@ -1159,7 +1179,7 @@ using the cookie store's secret. [Rick]
* Cookie session store: empty and unchanged sessions don't write a cookie. [Jeremy Kemper]
-* Added helper(:all) as a way to include all helpers from app/helpers/**/*.rb in ApplicationController [DHH]
+* Added helper(:all) as a way to include all helpers from app/helpers/**/*.rb in ApplicationController [David Heinemeier Hansson]
* Integration tests: introduce methods for other HTTP methods. #6353 [caboose]
@@ -1180,7 +1200,7 @@ Roos]
* Added .erb and .builder as preferred aliases to the now deprecated .rhtml and .rxml extensions [Chad Fowler]. This is done to separate the renderer from the mime type. .erb templates are often used to render emails, atom, csv, whatever. So labeling them .rhtml doesn't make too much sense. The same goes for .rxml, which can be used to build everything from HTML to Atom to whatever. .rhtml and .rxml will continue to work until Rails 3.0, though. So this is a slow phasing out. All generators and examples will start using the new aliases, though.
-* Added caching option to AssetTagHelper#stylesheet_link_tag and AssetTagHelper#javascript_include_tag [DHH]. Examples:
+* Added caching option to AssetTagHelper#stylesheet_link_tag and AssetTagHelper#javascript_include_tag [David Heinemeier Hansson]. Examples:
stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is false =>
<link href="/stylesheets/style1.css" media="screen" rel="Stylesheet" type="text/css" />
@@ -1195,13 +1215,13 @@ Roos]
* Work around the two connection per host browser limit: use asset%d.myapp.com to distribute asset requests among asset[0123].myapp.com. Use a DNS wildcard or CNAMEs to map these hosts to your asset server. See http://www.die.net/musings/page_load_time/ for background. [Jeremy Kemper]
-* Added default mime type for CSS (Mime::CSS) [DHH]
+* Added default mime type for CSS (Mime::CSS) [David Heinemeier Hansson]
-* Added that rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified and the response body will be set to an empty string. [DHH]
+* Added that rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified and the response body will be set to an empty string. [David Heinemeier Hansson]
-* Added X-Runtime to all responses with the request run time [DHH]
+* Added X-Runtime to all responses with the request run time [David Heinemeier Hansson]
-* Add Mime::Type convenience methods to check the current mime type. [Rick]
+* Add Mime::Type convenience methods to check the current mime type. [Rick Olson]
request.format.html? # => true if Mime::HTML
request.format.jpg? # => true if Mime::JPG
@@ -1214,16 +1234,16 @@ Roos]
* Add a :url_based_filename => true option to ActionController::Streaming::send_file, which allows URL-based filenames. [Thomas Fuchs]
-* Fix that FormTagHelper#submit_tag using :disable_with should trigger the onsubmit handler of its form if available [DHH]
+* Fix that FormTagHelper#submit_tag using :disable_with should trigger the onsubmit handler of its form if available [David Heinemeier Hansson]
-* Fix #render_file so that TemplateError is called with the correct params and you don't get the WSOD. [Rick]
+* Fix #render_file so that TemplateError is called with the correct params and you don't get the WSOD. [Rick Olson]
* Fix issue with deprecation messing up #template_root= usage. Add #prepend_view_path and #append_view_path to allow modification of a copy of the
-superclass' view_paths. [Rick]
+superclass' view_paths. [Rick Olson]
* Allow Controllers to have multiple view_paths instead of a single template_root. Closes #2754 [John Long]
-* Add much-needed html-scanner tests. Fixed CDATA parsing bug. [Rick]
+* Add much-needed html-scanner tests. Fixed CDATA parsing bug. [Rick Olson]
* improve error message for Routing for named routes. Closes #7346 [Rob Sanheim]
@@ -1231,15 +1251,15 @@ superclass' view_paths. [Rick]
* fix form_for example in ActionController::Resources documentation. Closes #7362 [gnarg]
-* Make sure that the string returned by TextHelper#truncate is actually a string, not a char proxy -- that should only be used internally while working on a multibyte-safe way of truncating [DHH]
+* Make sure that the string returned by TextHelper#truncate is actually a string, not a char proxy -- that should only be used internally while working on a multibyte-safe way of truncating [David Heinemeier Hansson]
-* Added FormBuilder#submit as a delegate for FormTagHelper#submit_tag [DHH]
+* Added FormBuilder#submit as a delegate for FormTagHelper#submit_tag [David Heinemeier Hansson]
* Allow Routes to generate all urls for a set of options by specifying :generate_all => true. Allows caching to properly set or expire all paths for a resource. References #1739. [Nicholas Seckar]
* Change the query parser to map empty GET params to "" rather than nil. Closes #5694. [Nicholas Seckar]
-* date_select and datetime_select take a :default option. #7052 [nik.wakelin]
+* date_select and datetime_select take a :default option. #7052 [Nik Wakelin]
date_select "post", "written_on", :default => 3.days.from_now
date_select "credit_card", "bill_due", :default => { :day => 20 }
@@ -1255,7 +1275,7 @@ superclass' view_paths. [Rick]
* Change session restoration to allow namespaced models to be autoloaded. Closes #6348. [Nicholas Seckar]
-* Fix doubly appearing parameters due to string and symbol mixups. Closes #2551. [aeden]
+* Fix doubly appearing parameters due to string and symbol mixups. Closes #2551. [Anthony Eden]
* Fix overly greedy rescues when loading helpers. Fixes #6268. [Nicholas Seckar]
@@ -1275,7 +1295,7 @@ superclass' view_paths. [Rick]
* Fix #distance_of_time_in_words to report accurately against the Duration class. #7114 [eventualbuddha]
-* Refactor #form_tag to allow easy extending. [Rick]
+* Refactor #form_tag to allow easy extending. [Rick Olson]
* Update to Prototype 1.5.0. [Sam Stephenson]
@@ -1287,11 +1307,11 @@ superclass' view_paths. [Rick]
* Allow exempt_from_layout :rhtml. #6742, #7026 [Dan Manges, Squeegy]
-* Recognize the .txt extension as Mime::TEXT [Rick]
+* Recognize the .txt extension as Mime::TEXT [Rick Olson]
* Fix parsing of array[] CGI parameters so extra empty values aren't included. #6252 [Nicholas Seckar, aiwilliams, brentrowland]
-* link_to_unless_current works with full URLs as well as paths. #6891 [Jarkko Laine, manfred, idrifter]
+* link_to_unless_current works with full URLs as well as paths. #6891 [Jarkko Laine, Manfred Stienstra, idrifter]
* Lookup the mime type for #auto_discovery_link_tag in the Mime::Type class. Closes #6941 [Josh Peek]
@@ -1337,7 +1357,7 @@ superclass' view_paths. [Rick]
* Unrescued ActiveRecord::RecordNotFound responds with 404 instead of 500. [Jeremy Kemper]
-* Improved auto_link to match more valid urls correctly [Tobias Luetke]
+* Improved auto_link to match more valid urls correctly [Tobias Lütke]
* Add singleton resources. [Rick Olson]
@@ -1360,7 +1380,7 @@ superclass' view_paths. [Rick]
* Add :index and @auto_index capability to model driven date/time selects. #847, #2655 [moriq, Doug Fales, Bob Silva]
-* Add :order to datetime_select, select_datetime, and select_date. #1427 [Timothee Peignier, patrick@lenz.sh, Bob Silva]
+* Add :order to datetime_select, select_datetime, and select_date. #1427 [Timothee Peignier, Patrick Lenz, Bob Silva]
* Added time_select to work with time values in models. Update scaffolding. #2489, #2833 [Justin Palmer, Andre Caum, Bob Silva]
@@ -1370,15 +1390,15 @@ superclass' view_paths. [Rick]
* Adds :time_separator to select_time and :date_separator to select_datetime. Preserves BC. #3811 [Bob Silva]
-* Added map.root as an alias for map.connect '' [DHH]
+* Added map.root as an alias for map.connect '' [David Heinemeier Hansson]
-* Added Request#format to return the format used for the request as a mime type. If no format is specified, the first Request#accepts type is used. This means you can stop using respond_to for anything else than responses [DHH]. Examples:
+* Added Request#format to return the format used for the request as a mime type. If no format is specified, the first Request#accepts type is used. This means you can stop using respond_to for anything else than responses [David Heinemeier Hansson]. Examples:
GET /posts/5.xml | request.format => Mime::XML
GET /posts/5.xhtml | request.format => Mime::HTML
GET /posts/5 | request.format => request.accepts.first (usually Mime::HTML for browsers)
-* Added the option for extension aliases to mime type registration [DHH]. Example (already in the default routes):
+* Added the option for extension aliases to mime type registration [David Heinemeier Hansson]. Example (already in the default routes):
Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
@@ -1388,9 +1408,9 @@ superclass' view_paths. [Rick]
* Added CSV to Mime::SET so that respond_to csv will work [Cody Fauser]
-* Fixed that HEAD should return the proper Content-Length header (that is, actually use @body.size, not just 0) [DHH]
+* Fixed that HEAD should return the proper Content-Length header (that is, actually use @body.size, not just 0) [David Heinemeier Hansson]
-* Added GET-masquarading for HEAD, so request.method will return :get even for HEADs. This will help anyone relying on case request.method to automatically work with HEAD and map.resources will also allow HEADs to all GET actions. Rails automatically throws away the response content in a reply to HEAD, so you don't even need to worry about that. If you, for whatever reason, still need to distinguish between GET and HEAD in some edge case, you can use Request#head? and even Request.headers["REQUEST_METHOD"] for get the "real" answer. Closes #6694 [DHH]
+* Added GET-masquarading for HEAD, so request.method will return :get even for HEADs. This will help anyone relying on case request.method to automatically work with HEAD and map.resources will also allow HEADs to all GET actions. Rails automatically throws away the response content in a reply to HEAD, so you don't even need to worry about that. If you, for whatever reason, still need to distinguish between GET and HEAD in some edge case, you can use Request#head? and even Request.headers["REQUEST_METHOD"] for get the "real" answer. Closes #6694 [David Heinemeier Hansson]
* Update Routing to complain when :controller is not specified by a route. Closes #6669. [Nicholas Seckar]
@@ -1439,13 +1459,13 @@ superclass' view_paths. [Rick]
* Update to latest Prototype, which doesn't serialize disabled form elements, adds clone() to arrays, empty/non-string Element.update() and adds a fixes excessive error reporting in WebKit beta versions [Thomas Fuchs]
-* Deprecate start_form_tag and end_form_tag. Use form_tag / '</form>' from now on. [Rick]
+* Deprecate start_form_tag and end_form_tag. Use form_tag / '</form>' from now on. [Rick Olson]
-* Added block-usage to PrototypeHelper#form_remote_tag, document block-usage of FormTagHelper#form_tag [Rick]
+* Added block-usage to PrototypeHelper#form_remote_tag, document block-usage of FormTagHelper#form_tag [Rick Olson]
-* Add a 0 margin/padding div around the hidden _method input tag that form_tag outputs. [Rick]
+* Add a 0 margin/padding div around the hidden _method input tag that form_tag outputs. [Rick Olson]
-* Added block-usage to TagHelper#content_tag [DHH]. Example:
+* Added block-usage to TagHelper#content_tag [David Heinemeier Hansson]. Example:
<% content_tag :div, :class => "strong" %>
Hello world!
@@ -1454,13 +1474,13 @@ superclass' view_paths. [Rick]
Will output:
<div class="strong">Hello world!</div>
-* Deprecated UrlHelper#link_to_image and UrlHelper#link_to :post => true #6409 [BobSilva]
+* Deprecated UrlHelper#link_to_image and UrlHelper#link_to :post => true #6409 [Bob Silva]
-* Upgraded NumberHelper with number_to_phone support international formats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) #6421 [BobSilva]
+* Upgraded NumberHelper with number_to_phone support international formats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) #6421 [Bob Silva]
-* Fixed that setting RAILS_ASSET_ID to "" should not add a trailing slash after assets #6454 [BobSilva/chrismear]
+* Fixed that setting RAILS_ASSET_ID to "" should not add a trailing slash after assets #6454 [Bob Silva/chrismear]
-* Force *_url named routes to show the host in ActionView [Rick]
+* Force *_url named routes to show the host in ActionView [Rick Olson]
<%= url_for ... %> # no host
<%= foo_path %> # no host
@@ -1470,9 +1490,9 @@ superclass' view_paths. [Rick]
* Add JavaScriptGenerator#literal for wrapping a string in an object whose #to_json is the string itself. [Sam Stephenson]
-* Add <%= escape_once html %> to escape html while leaving any currently escaped entities alone. Fix button_to double-escaping issue. [Rick]
+* Add <%= escape_once html %> to escape html while leaving any currently escaped entities alone. Fix button_to double-escaping issue. [Rick Olson]
-* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc. [Rick]
+* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc. [Rick Olson]
* Fix deprecation warnings when rendering the template error template. [Nicholas Seckar]
@@ -1498,7 +1518,7 @@ superclass' view_paths. [Rick]
* Rename test assertion to prevent shadowing. Closes #6306. [psross]
-* Fixed that NumberHelper#number_to_delimiter should respect precision of higher than two digits #6231 [phallstrom]
+* Fixed that NumberHelper#number_to_delimiter should respect precision of higher than two digits #6231 [Philip Hallstrom]
* Fixed that FormHelper#radio_button didn't respect an :id being passed in #6266 [evansj]
@@ -1512,7 +1532,7 @@ superclass' view_paths. [Rick]
Which is needed for dealing with the IE6 DOM when it's not yet fully loaded.
-* Fixed that rescue template path shouldn't be hardcoded, then it's easier to hook in your own #6295 [mnaberez]
+* Fixed that rescue template path shouldn't be hardcoded, then it's easier to hook in your own #6295 [Mike Naberezny]
* Fixed escaping of backslashes in JavaScriptHelper#escape_javascript #6302 [sven@c3d2.de]
@@ -1553,7 +1573,7 @@ superclass' view_paths. [Rick]
* Rescue Errno::ECONNRESET to handle an unexpectedly closed socket connection. Improves SCGI reliability. #3368, #6226 [sdsykes, fhanshaw@vesaria.com]
-* Added that respond_to blocks will automatically set the content type to be the same as is requested [DHH]. Examples:
+* Added that respond_to blocks will automatically set the content type to be the same as is requested [David Heinemeier Hansson]. Examples:
respond_to do |format|
format.html { render :text => "I'm being sent as text/html" }
@@ -1561,9 +1581,9 @@ superclass' view_paths. [Rick]
format.atom { render :text => "I'm being sent as application/xml", :content_type => Mime::XML }
end
-* Added utf-8 as the default charset for all renders. You can change this default using ActionController::Base.default_charset=(encoding) [DHH]
+* Added utf-8 as the default charset for all renders. You can change this default using ActionController::Base.default_charset=(encoding) [David Heinemeier Hansson]
-* Added proper getters and setters for content type and charset [DHH]. Example of what we used to do:
+* Added proper getters and setters for content type and charset [David Heinemeier Hansson]. Example of what we used to do:
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
@@ -1598,7 +1618,7 @@ superclass' view_paths. [Rick]
* Load helpers in alphabetical order for consistency. Resolve cyclic javascript_helper dependency. #6132, #6178 [choonkeat@gmail.com]
-* Skip params with empty names, such as the &=Save query string from <input type="submit"/>. #2569 [manfred, raphinou@yahoo.com]
+* Skip params with empty names, such as the &=Save query string from <input type="submit"/>. #2569 [Manfred Stienstra, raphinou@yahoo.com]
* Fix assert_tag so that :content => "foo" does not match substrings, but only exact strings. Use :content => /foo/ to match substrings. #2799 [Eric Hodel]
@@ -1608,7 +1628,7 @@ superclass' view_paths. [Rick]
* Update UrlWriter to support :only_path. [Nicholas Seckar, Dave Thomas]
-* Fixed JavaScriptHelper#link_to_function and JavaScriptHelper#button_to_function to have the script argument be optional [DHH]. So what used to require a nil, like this:
+* Fixed JavaScriptHelper#link_to_function and JavaScriptHelper#button_to_function to have the script argument be optional [David Heinemeier Hansson]. So what used to require a nil, like this:
link_to("Hider", nil, :class => "hider_link") { |p| p[:something].hide }
@@ -1629,7 +1649,7 @@ superclass' view_paths. [Rick]
* Fixed that AssetTagHelper#image_tag and others using compute_public_path should not modify the incoming source argument (closes #5102) [eule@space.ch]
-* Deprecated the auto-appending of .png to AssetTagHelper#image_tag calls that doesn't have an extension [DHH]
+* Deprecated the auto-appending of .png to AssetTagHelper#image_tag calls that doesn't have an extension [David Heinemeier Hansson]
* Fixed FormOptionsHelper#select to respect :selected value #5813
@@ -1639,16 +1659,16 @@ superclass' view_paths. [Rick]
* Improved resolution of DateHelper#distance_of_time_in_words for better precision #5994 [Bob Silva]
-* Changed that uncaught exceptions raised any where in the application will cause RAILS_ROOT/public/500.html to be read and shown instead of just the static "Application error (Rails)" [DHH]
+* Changed that uncaught exceptions raised any where in the application will cause RAILS_ROOT/public/500.html to be read and shown instead of just the static "Application error (Rails)" [David Heinemeier Hansson]
* Integration tests: thoroughly test ActionController::Integration::Session. #6022 [Kevin Clark]
(tests skipped unless you `gem install mocha`)
-* Added deprecation language for pagination which will become a plugin by Rails 2.0 [DHH]
+* Added deprecation language for pagination which will become a plugin by Rails 2.0 [David Heinemeier Hansson]
-* Added deprecation language for in_place_editor and auto_complete_field that both pieces will become plugins by Rails 2.0 [DHH]
+* Added deprecation language for in_place_editor and auto_complete_field that both pieces will become plugins by Rails 2.0 [David Heinemeier Hansson]
-* Deprecated all of ActionController::Dependencies. All dependency loading is now handled from Active Support [DHH]
+* Deprecated all of ActionController::Dependencies. All dependency loading is now handled from Active Support [David Heinemeier Hansson]
* Added assert_select* for CSS selector-based testing (deprecates assert_tag) #5936 [assaf.arkin@gmail.com]
@@ -1666,7 +1686,7 @@ superclass' view_paths. [Rick]
* Add routing tests to assert that RoutingError is raised when conditions aren't met. Closes #6016 [Nathan Witmer]
-* Deprecation: update docs. #5998 [jakob@mentalized.net, Kevin Clark]
+* Deprecation: update docs. #5998 [Jakob Skjerning, Kevin Clark]
* Make auto_link parse a greater subset of valid url formats. [Jamis Buck]
@@ -1676,15 +1696,15 @@ superclass' view_paths. [Rick]
* Fix send_data documentation typo. #5982 [brad@madriska.com]
-* Switch to using FormEncodedPairParser for parsing request parameters. [Nicholas Seckar, DHH]
+* Switch to using FormEncodedPairParser for parsing request parameters. [Nicholas Seckar, David Heinemeier Hansson]
-* respond_to .html now always renders #{action_name}.rhtml so that registered custom template handlers do not override it in priority. Custom mime types require a block and throw proper error now. [Tobias Luetke]
+* respond_to .html now always renders #{action_name}.rhtml so that registered custom template handlers do not override it in priority. Custom mime types require a block and throw proper error now. [Tobias Lütke]
* Deprecation: test deprecated instance vars in partials. [Jeremy Kemper]
* Add UrlWriter to allow writing urls from Mailers and scripts. [Nicholas Seckar]
-* Clean up and run the Active Record integration tests by default. #5854 [kevin.clark@gmail.com, Jeremy Kemper]
+* Clean up and run the Active Record integration tests by default. #5854 [Kevin Clark, Jeremy Kemper]
* Correct example in cookies docs. #5832 [jessemerriman@warpmail.net]
@@ -1712,7 +1732,7 @@ superclass' view_paths. [Rick]
* Deprecation! @params, @session, @flash will be removed after 1.2. Use the corresponding instance methods instead. You'll get printed warnings during tests and logged warnings in dev mode when you access either instance variable directly. [Jeremy Kemper]
-* Make Routing noisy when an anchor regexp is assigned to a segment. #5674 [francois.beausoleil@gmail.com]
+* Make Routing noisy when an anchor regexp is assigned to a segment. #5674 [François Beausoleil]
* Added months and years to the resolution of DateHelper#distance_of_time_in_words, such that "60 days ago" becomes "2 months ago" #5611 [pjhyett@gmail.com]
@@ -1732,7 +1752,7 @@ superclass' view_paths. [Rick]
* Fixed the new_#{resource}_url route and added named route tests for Simply Restful. [Rick Olson]
-* Added map.resources from the Simply Restful plugin [DHH]. Examples (the API has changed to use plurals!):
+* Added map.resources from the Simply Restful plugin [David Heinemeier Hansson]. Examples (the API has changed to use plurals!):
map.resources :messages
map.resources :messages, :comments
@@ -1740,13 +1760,13 @@ superclass' view_paths. [Rick]
* Fixed that integration simulation of XHRs should set Accept header as well [Edward Frederick]
-* TestRequest#reset_session should restore a TestSession, not a hash [Koz]
+* TestRequest#reset_session should restore a TestSession, not a hash [Michael Koziarski]
* Don't search a load-path of '.' for controller files [Jamis Buck]
* Update integration.rb to require test_process explicitly instead of via Dependencies. [Nicholas Seckar]
-* Fixed that you can still access the flash after the flash has been reset in reset_session. Closes #5584 [lmarlow@yahoo.com]
+* Fixed that you can still access the flash after the flash has been reset in reset_session. Closes #5584 [lmarlow]
* Allow form_for and fields_for to work with indexed form inputs. [Jeremy Kemper, Matt Lyon]
@@ -1755,13 +1775,13 @@ superclass' view_paths. [Rick]
* Remove leak in development mode by replacing define_method with module_eval. [Nicholas Seckar]
-* Provide support for decimal columns to form helpers. Closes #5672. [dave@pragprog.com]
+* Provide support for decimal columns to form helpers. Closes #5672. [Dave Thomas]
* Update documentation for erb trim syntax. #5651 [matt@mattmargolis.net]
* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com, sebastien@goetzilla.info]
-* Reset @html_document between requests so assert_tag works. #4810 [jarkko@jlaine.net, easleydp@gmail.com]
+* Reset @html_document between requests so assert_tag works. #4810 [Jarkko Laine, easleydp@gmail.com]
* Update render :partial documentation. #5646 [matt@mattmargolis.net]
@@ -1771,21 +1791,21 @@ superclass' view_paths. [Rick]
* Fixed that real files and symlinks should be treated the same when compiling templates #5438 [zachary@panandscan.com]
-* Fixed that the flash should be reset when reset_session is called #5584 [shugo@ruby-lang.org]
+* Fixed that the flash should be reset when reset_session is called #5584 [Shugo Maeda]
* Added special case for "1 Byte" in NumberHelper#number_to_human_size #5593 [murpyh@rubychan.de]
-* Fixed proper form-encoded parameter parsing for requests with "Content-Type: application/x-www-form-urlencoded; charset=utf-8" (note the presence of a charset directive) [DHH]
+* Fixed proper form-encoded parameter parsing for requests with "Content-Type: application/x-www-form-urlencoded; charset=utf-8" (note the presence of a charset directive) [David Heinemeier Hansson]
* Add route_name_path method to generate only the path for a named routes. For example, map.person will add person_path. [Nicholas Seckar]
* Avoid naming collision among compiled view methods. [Jeremy Kemper]
-* Fix CGI extensions when they expect string but get nil in Windows. Closes #5276 [mislav@nippur.irb.hr]
+* Fix CGI extensions when they expect string but get nil in Windows. Closes #5276 [Mislav Marohnić]
* Determine the correct template_root for deeply nested components. #2841 [s.brink@web.de]
-* Fix that routes with *path segments in the recall can generate URLs. [Rick]
+* Fix that routes with *path segments in the recall can generate URLs. [Rick Olson]
* Fix strip_links so that it doesn't hang on multiline <acronym> tags [Jamis Buck]
@@ -1803,11 +1823,11 @@ superclass' view_paths. [Rick]
* Rewind readable CGI params so others may reread them (such as CGI::Session when passing the session id in a multipart form). #210 [mklame@atxeu.com, matthew@walker.wattle.id.au]
-* Added Mime::TEXT (text/plain) and Mime::ICS (text/calendar) as new default types [DHH]
+* Added Mime::TEXT (text/plain) and Mime::ICS (text/calendar) as new default types [David Heinemeier Hansson]
-* Added Mime::Type.register(string, symbol, synonyms = []) for adding new custom mime types [DHH]. Example: Mime::Type.register("image/gif", :gif)
+* Added Mime::Type.register(string, symbol, synonyms = []) for adding new custom mime types [David Heinemeier Hansson]. Example: Mime::Type.register("image/gif", :gif)
-* Added support for Mime objects in render :content_type option [DHH]. Example: render :text => some_atom, :content_type => Mime::ATOM
+* Added support for Mime objects in render :content_type option [David Heinemeier Hansson]. Example: render :text => some_atom, :content_type => Mime::ATOM
* Add :status option to send_data and send_file. Defaults to '200 OK'. #5243 [Manfred Stienstra <m.stienstra@fngtps.com>]
@@ -1821,7 +1841,7 @@ superclass' view_paths. [Rick]
* Accept multipart PUT parameters. #5235 [guy.naor@famundo.com]
-* Added interrogation of params[:format] to determine Accept type. If :format is specified and matches a declared extension, like "rss" or "xml", that mime type will be put in front of the accept handler. This means you can link to the same action from different extensions and use that fact to determine output [DHH]. Example:
+* Added interrogation of params[:format] to determine Accept type. If :format is specified and matches a declared extension, like "rss" or "xml", that mime type will be put in front of the accept handler. This means you can link to the same action from different extensions and use that fact to determine output [David Heinemeier Hansson]. Example:
class WeblogController < ActionController::Base
def index
@@ -1854,13 +1874,13 @@ superclass' view_paths. [Rick]
All this relies on the fact that you have a route that includes .:format.
-* Expanded :method option in FormTagHelper#form_tag, FormHelper#form_for, PrototypeHelper#remote_form_for, PrototypeHelper#remote_form_tag, and PrototypeHelper#link_to_remote to allow for verbs other than GET and POST by automatically creating a hidden form field named _method, which will simulate the other verbs over post [DHH]
+* Expanded :method option in FormTagHelper#form_tag, FormHelper#form_for, PrototypeHelper#remote_form_for, PrototypeHelper#remote_form_tag, and PrototypeHelper#link_to_remote to allow for verbs other than GET and POST by automatically creating a hidden form field named _method, which will simulate the other verbs over post [David Heinemeier Hansson]
-* Added :method option to UrlHelper#link_to, which allows for using other verbs than GET for the link. This replaces the :post option, which is now deprecated. Example: link_to "Destroy", person_url(:id => person), :method => :delete [DHH]
+* Added :method option to UrlHelper#link_to, which allows for using other verbs than GET for the link. This replaces the :post option, which is now deprecated. Example: link_to "Destroy", person_url(:id => person), :method => :delete [David Heinemeier Hansson]
* follow_redirect doesn't complain about being redirected to the same controller. #5153 [dymo@mk.ukrtelecom.ua]
-* Add layout attribute to response object with the name of the layout that was rendered, or nil if none rendered. [Kevin Clark kevin.clark@gmail.com]
+* Add layout attribute to response object with the name of the layout that was rendered, or nil if none rendered. [Kevin Clark]
* Fix NoMethodError when parsing params like &&. [Adam Greenfield]
@@ -1870,7 +1890,7 @@ superclass' view_paths. [Rick]
* Excise ingrown code from FormOptionsHelper#options_for_select. #5008 [anonymous]
-* Small fix in routing to allow dynamic routes (broken after [4242]) [Rick]
+* Small fix in routing to allow dynamic routes (broken after [4242]) [Rick Olson]
map.connect '*path', :controller => 'files', :action => 'show'
@@ -1882,7 +1902,7 @@ superclass' view_paths. [Rick]
* Documentation fix: integration test scripts don't require integration_test. Closes #4914. [Frederick Ros <sl33p3r@free.fr>]
-* ActionController::Base Summary documentation rewrite. Closes #4900. [kevin.clark@gmail.com]
+* ActionController::Base Summary documentation rewrite. Closes #4900. [Kevin Clark]
* Fix text_helper.rb documentation rendering. Closes #4725. [Frederick Ros]
@@ -1894,9 +1914,9 @@ superclass' view_paths. [Rick]
* Enhance documentation for setting headers in integration tests. Skip auto HTTP prepending when its already there. Closes #4079. [Rick Olson]
-* Documentation for AbstractRequest. Closes #4895. [kevin.clark@gmail.com]
+* Documentation for AbstractRequest. Closes #4895. [Kevin Clark]
-* Refactor various InstanceTag instance method to class methods. Closes #4800. [skaes@web.de]
+* Refactor various InstanceTag instance method to class methods. Closes #4800. [Stefan Kaes]
* Remove all remaining references to @params in the documentation. [Marcel Molina Jr.]
@@ -1904,25 +1924,25 @@ superclass' view_paths. [Rick]
* Update layout and content_for documentation to use yield rather than magic @content_for instance variables. [Marcel Molina Jr.]
-* Fix assert_redirected_to tests according to real-world usage. Also, don't fail if you add an extra :controller option: [Rick]
+* Fix assert_redirected_to tests according to real-world usage. Also, don't fail if you add an extra :controller option: [Rick Olson]
redirect_to :action => 'new'
assert_redirected_to :controller => 'monkeys', :action => 'new'
-* Cache CgiRequest#request_parameters so that multiple calls don't re-parse multipart data. [Rick]
+* Cache CgiRequest#request_parameters so that multiple calls don't re-parse multipart data. [Rick Olson]
-* Diff compared routing options. Allow #assert_recognizes to take a second arg as a hash to specify optional request method [Rick]
+* Diff compared routing options. Allow #assert_recognizes to take a second arg as a hash to specify optional request method [Rick Olson]
assert_recognizes({:controller => 'users', :action => 'index'}, 'users')
assert_recognizes({:controller => 'users', :action => 'create'}, {:path => 'users', :method => :post})
-* Diff compared options with #assert_redirected_to [Rick]
+* Diff compared options with #assert_redirected_to [Rick Olson]
* Add support in routes for semicolon delimited "subpaths", like /books/:id;:action [Jamis Buck]
* Change link_to_function and button_to_function to (optionally) take an update_page block instead of a JavaScript string. Closes #4804. [zraii@comcast.net, Sam Stephenson]
-* Fixed that remote_form_for can leave out the object parameter and default to the instance variable of the object_name, just like form_for [DHH]
+* Fixed that remote_form_for can leave out the object parameter and default to the instance variable of the object_name, just like form_for [David Heinemeier Hansson]
* Modify routing so that you can say :require => { :method => :post } for a route, and the route will never be selected unless the request method is POST. Only works for route recognition, not for route generation. [Jamis Buck]
@@ -1938,14 +1958,14 @@ superclass' view_paths. [Rick]
* Apply [5709] to stable.
-* session_enabled? works with session :off. #6680 [Catfish]
+* session_enabled? works with session :off. #6680 [Jonathan del Strother]
* Performance: patch cgi/session to require digest/md5 once rather than per #create_new_id. [Stefan Kaes]
*1.13.2* (February 5th, 2007)
-* Add much-needed html-scanner tests. Fixed CDATA parsing bug. [Rick]
+* Add much-needed html-scanner tests. Fixed CDATA parsing bug. [Rick Olson]
* improve error message for Routing for named routes. [Rob Sanheim]
@@ -1967,7 +1987,7 @@ superclass' view_paths. [Rick]
* Make TextHelper::auto_link recognize URLs with colons in path correctly, fixes #7268. [imajes]
-* Improved auto_link to match more valid urls correctly [Tobias Luetke]
+* Improved auto_link to match more valid urls correctly [Tobias Lütke]
*1.13.1* (January 18th, 2007)
@@ -1983,11 +2003,11 @@ superclass' view_paths. [Rick]
* Update to Prototype 1.5.0. [Sam Stephenson]
-* Allow exempt_from_layout :rhtml. #6742, #7026 [dcmanges, Squeegy]
+* Allow exempt_from_layout :rhtml. #6742, #7026 [Dan Manges, Squeegy]
* Fix parsing of array[] CGI parameters so extra empty values aren't included. #6252 [Nicholas Seckar, aiwilliams, brentrowland]
-* link_to_unless_current works with full URLs as well as paths. #6891 [Jarkko Laine, manfred, idrifter]
+* link_to_unless_current works with full URLs as well as paths. #6891 [Jarkko Laine, Manfred Stienstra, idrifter]
* Fix HTML::Node to output double quotes instead of single quotes. Closes #6845 [mitreandy]
@@ -2029,7 +2049,7 @@ superclass' view_paths. [Rick]
* Add :index and @auto_index capability to model driven date/time selects. #847, #2655 [moriq, Doug Fales, Bob Silva]
-* Add :order to datetime_select, select_datetime, and select_date. #1427 [Timothee Peignier, patrick@lenz.sh, Bob Silva]
+* Add :order to datetime_select, select_datetime, and select_date. #1427 [Timothee Peignier, Patrick Lenz, Bob Silva]
* Added time_select to work with time values in models. Update scaffolding. #2489, #2833 [Justin Palmer, Andre Caum, Bob Silva]
@@ -2041,9 +2061,9 @@ superclass' view_paths. [Rick]
* @response.redirect_url works with 201 Created responses: just return headers['Location'] rather than checking the response status. [Jeremy Kemper]
-* Fixed that HEAD should return the proper Content-Length header (that is, actually use @body.size, not just 0) [DHH]
+* Fixed that HEAD should return the proper Content-Length header (that is, actually use @body.size, not just 0) [David Heinemeier Hansson]
-* Added GET-masquarading for HEAD, so request.method will return :get even for HEADs. This will help anyone relying on case request.method to automatically work with HEAD and map.resources will also allow HEADs to all GET actions. Rails automatically throws away the response content in a reply to HEAD, so you don't even need to worry about that. If you, for whatever reason, still need to distinguish between GET and HEAD in some edge case, you can use Request#head? and even Request.headers["REQUEST_METHOD"] for get the "real" answer. Closes #6694 [DHH]
+* Added GET-masquarading for HEAD, so request.method will return :get even for HEADs. This will help anyone relying on case request.method to automatically work with HEAD and map.resources will also allow HEADs to all GET actions. Rails automatically throws away the response content in a reply to HEAD, so you don't even need to worry about that. If you, for whatever reason, still need to distinguish between GET and HEAD in some edge case, you can use Request#head? and even Request.headers["REQUEST_METHOD"] for get the "real" answer. Closes #6694 [David Heinemeier Hansson]
*1.13.0 RC1* (r5619, November 22nd, 2006)
@@ -2083,13 +2103,13 @@ superclass' view_paths. [Rick]
* Deprecate expire_matched_fragments. Use expire_fragment instead. #6535 [Bob Silva]
-* Deprecate start_form_tag and end_form_tag. Use form_tag / '</form>' from now on. [Rick]
+* Deprecate start_form_tag and end_form_tag. Use form_tag / '</form>' from now on. [Rick Olson]
-* Added block-usage to PrototypeHelper#form_remote_tag, document block-usage of FormTagHelper#form_tag [Rick]
+* Added block-usage to PrototypeHelper#form_remote_tag, document block-usage of FormTagHelper#form_tag [Rick Olson]
-* Add a 0 margin/padding div around the hidden _method input tag that form_tag outputs. [Rick]
+* Add a 0 margin/padding div around the hidden _method input tag that form_tag outputs. [Rick Olson]
-* Added block-usage to TagHelper#content_tag [DHH]. Example:
+* Added block-usage to TagHelper#content_tag [David Heinemeier Hansson]. Example:
<% content_tag :div, :class => "strong" %>
Hello world!
@@ -2098,13 +2118,13 @@ superclass' view_paths. [Rick]
Will output:
<div class="strong">Hello world!</div>
-* Deprecated UrlHelper#link_to_image and UrlHelper#link_to :post => true #6409 [BobSilva]
+* Deprecated UrlHelper#link_to_image and UrlHelper#link_to :post => true #6409 [Bob Silva]
-* Upgraded NumberHelper with number_to_phone support international formats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) #6421 [BobSilva]
+* Upgraded NumberHelper with number_to_phone support international formats to comply with ITU E.123 by supporting area codes with less than 3 digits, added precision argument to number_to_human_size (defaults to 1) #6421 [Bob Silva]
-* Fixed that setting RAILS_ASSET_ID to "" should not add a trailing slash after assets #6454 [BobSilva/chrismear]
+* Fixed that setting RAILS_ASSET_ID to "" should not add a trailing slash after assets #6454 [Bob Silva/chrismear]
-* Force *_url named routes to show the host in ActionView [Rick]
+* Force *_url named routes to show the host in ActionView [Rick Olson]
<%= url_for ... %> # no host
<%= foo_path %> # no host
@@ -2114,9 +2134,9 @@ superclass' view_paths. [Rick]
* Add JavaScriptGenerator#literal for wrapping a string in an object whose #to_json is the string itself. [Sam Stephenson]
-* Add <%= escape_once html %> to escape html while leaving any currently escaped entities alone. Fix button_to double-escaping issue. [Rick]
+* Add <%= escape_once html %> to escape html while leaving any currently escaped entities alone. Fix button_to double-escaping issue. [Rick Olson]
-* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc. [Rick]
+* Fix double-escaped entities, such as &amp;amp;, &amp;#123;, etc. [Rick Olson]
* Fix routing to correctly determine when generation fails. Closes #6300. [psross].
@@ -2136,7 +2156,7 @@ superclass' view_paths. [Rick]
* Rename test assertion to prevent shadowing. Closes #6306. [psross]
-* Fixed that NumberHelper#number_to_delimiter should respect precision of higher than two digits #6231 [phallstrom]
+* Fixed that NumberHelper#number_to_delimiter should respect precision of higher than two digits #6231 [Philip Hallstrom]
* Fixed that FormHelper#radio_button didn't respect an :id being passed in #6266 [evansj]
@@ -2150,7 +2170,7 @@ superclass' view_paths. [Rick]
Which is needed for dealing with the IE6 DOM when it's not yet fully loaded.
-* Fixed that rescue template path shouldn't be hardcoded, then it's easier to hook in your own #6295 [mnaberez]
+* Fixed that rescue template path shouldn't be hardcoded, then it's easier to hook in your own #6295 [Mike Naberezny]
* Fixed escaping of backslashes in JavaScriptHelper#escape_javascript #6302 [sven@c3d2.de]
@@ -2189,7 +2209,7 @@ superclass' view_paths. [Rick]
* Rescue Errno::ECONNRESET to handle an unexpectedly closed socket connection. Improves SCGI reliability. #3368, #6226 [sdsykes, fhanshaw@vesaria.com]
-* Added that respond_to blocks will automatically set the content type to be the same as is requested [DHH]. Examples:
+* Added that respond_to blocks will automatically set the content type to be the same as is requested [David Heinemeier Hansson]. Examples:
respond_to do |format|
format.html { render :text => "I'm being sent as text/html" }
@@ -2197,9 +2217,9 @@ superclass' view_paths. [Rick]
format.atom { render :text => "I'm being sent as application/xml", :content_type => Mime::XML }
end
-* Added utf-8 as the default charset for all renders. You can change this default using ActionController::Base.default_charset=(encoding) [DHH]
+* Added utf-8 as the default charset for all renders. You can change this default using ActionController::Base.default_charset=(encoding) [David Heinemeier Hansson]
-* Added proper getters and setters for content type and charset [DHH]. Example of what we used to do:
+* Added proper getters and setters for content type and charset [David Heinemeier Hansson]. Example of what we used to do:
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
@@ -2228,7 +2248,7 @@ superclass' view_paths. [Rick]
* Load helpers in alphabetical order for consistency. Resolve cyclic javascript_helper dependency. #6132, #6178 [choonkeat@gmail.com]
-* Skip params with empty names, such as the &=Save query string from <input type="submit"/>. #2569 [manfred, raphinou@yahoo.com]
+* Skip params with empty names, such as the &=Save query string from <input type="submit"/>. #2569 [Manfred Stienstra, raphinou@yahoo.com]
* Fix assert_tag so that :content => "foo" does not match substrings, but only exact strings. Use :content => /foo/ to match substrings. #2799 [Eric Hodel]
@@ -2236,7 +2256,7 @@ superclass' view_paths. [Rick]
* Update UrlWriter to support :only_path. [Nicholas Seckar, Dave Thomas]
-* Fixed JavaScriptHelper#link_to_function and JavaScriptHelper#button_to_function to have the script argument be optional [DHH]. So what used to require a nil, like this:
+* Fixed JavaScriptHelper#link_to_function and JavaScriptHelper#button_to_function to have the script argument be optional [David Heinemeier Hansson]. So what used to require a nil, like this:
link_to("Hider", nil, :class => "hider_link") { |p| p[:something].hide }
@@ -2253,7 +2273,7 @@ superclass' view_paths. [Rick]
* Fixed that AssetTagHelper#image_tag and others using compute_public_path should not modify the incoming source argument (closes #5102) [eule@space.ch]
-* Deprecated the auto-appending of .png to AssetTagHelper#image_tag calls that doesn't have an extension [DHH]
+* Deprecated the auto-appending of .png to AssetTagHelper#image_tag calls that doesn't have an extension [David Heinemeier Hansson]
* Fixed FormOptionsHelper#select to respect :selected value #5813
@@ -2263,13 +2283,13 @@ superclass' view_paths. [Rick]
* Improved resolution of DateHelper#distance_of_time_in_words for better precision #5994 [Bob Silva]
-* Changed that uncaught exceptions raised any where in the application will cause RAILS_ROOT/public/500.html to be read and shown instead of just the static "Application error (Rails)" [DHH]
+* Changed that uncaught exceptions raised any where in the application will cause RAILS_ROOT/public/500.html to be read and shown instead of just the static "Application error (Rails)" [David Heinemeier Hansson]
-* Added deprecation language for pagination which will become a plugin by Rails 2.0 [DHH]
+* Added deprecation language for pagination which will become a plugin by Rails 2.0 [David Heinemeier Hansson]
-* Added deprecation language for in_place_editor and auto_complete_field that both pieces will become plugins by Rails 2.0 [DHH]
+* Added deprecation language for in_place_editor and auto_complete_field that both pieces will become plugins by Rails 2.0 [David Heinemeier Hansson]
-* Deprecated all of ActionController::Dependencies. All dependency loading is now handled from Active Support [DHH]
+* Deprecated all of ActionController::Dependencies. All dependency loading is now handled from Active Support [David Heinemeier Hansson]
* Added assert_select* for CSS selector-based testing (deprecates assert_tag) #5936 [assaf.arkin@gmail.com]
@@ -2289,9 +2309,9 @@ superclass' view_paths. [Rick]
* Integration tests: headers beginning with X aren't excluded from the HTTP_ prefix, so X-Requested-With becomes HTTP_X_REQUESTED_WITH as expected. [Mike Clark]
-* Switch to using FormEncodedPairParser for parsing request parameters. [Nicholas Seckar, DHH]
+* Switch to using FormEncodedPairParser for parsing request parameters. [Nicholas Seckar, David Heinemeier Hansson]
-* respond_to .html now always renders #{action_name}.rhtml so that registered custom template handlers do not override it in priority. Custom mime types require a block and throw proper error now. [Tobias Luetke]
+* respond_to .html now always renders #{action_name}.rhtml so that registered custom template handlers do not override it in priority. Custom mime types require a block and throw proper error now. [Tobias Lütke]
* Deprecation: test deprecated instance vars in partials. [Jeremy Kemper]
@@ -2317,7 +2337,7 @@ superclass' view_paths. [Rick]
* Deprecation! @params, @session, @flash will be removed after 1.2. Use the corresponding instance methods instead. You'll get printed warnings during tests and logged warnings in dev mode when you access either instance variable directly. [Jeremy Kemper]
-* Make Routing noisy when an anchor regexp is assigned to a segment. #5674 [francois.beausoleil@gmail.com]
+* Make Routing noisy when an anchor regexp is assigned to a segment. #5674 [François Beausoleil]
* Added months and years to the resolution of DateHelper#distance_of_time_in_words, such that "60 days ago" becomes "2 months ago" #5611 [pjhyett@gmail.com]
@@ -2331,7 +2351,7 @@ superclass' view_paths. [Rick]
* Fixed the new_#{resource}_url route and added named route tests for Simply Restful. [Rick Olson]
-* Added map.resources from the Simply Restful plugin [DHH]. Examples (the API has changed to use plurals!):
+* Added map.resources from the Simply Restful plugin [David Heinemeier Hansson]. Examples (the API has changed to use plurals!):
map.resources :messages
map.resources :messages, :comments
@@ -2339,13 +2359,13 @@ superclass' view_paths. [Rick]
* Fixed that integration simulation of XHRs should set Accept header as well [Edward Frederick]
-* TestRequest#reset_session should restore a TestSession, not a hash [Koz]
+* TestRequest#reset_session should restore a TestSession, not a hash [Michael Koziarski]
* Don't search a load-path of '.' for controller files [Jamis Buck]
* Update integration.rb to require test_process explicitly instead of via Dependencies. [Nicholas Seckar]
-* Fixed that you can still access the flash after the flash has been reset in reset_session. Closes #5584 [lmarlow@yahoo.com]
+* Fixed that you can still access the flash after the flash has been reset in reset_session. Closes #5584 [lmarlow]
* Allow form_for and fields_for to work with indexed form inputs. [Jeremy Kemper, Matt Lyon]
@@ -2354,11 +2374,11 @@ superclass' view_paths. [Rick]
* Remove leak in development mode by replacing define_method with module_eval. [Nicholas Seckar]
-* Provide support for decimal columns to form helpers. Closes #5672. [dave@pragprog.com]
+* Provide support for decimal columns to form helpers. Closes #5672. [Dave Thomas]
* Pass :id => nil or :class => nil to error_messages_for to supress that html attribute. #3586 [olivier_ansaldi@yahoo.com, sebastien@goetzilla.info]
-* Reset @html_document between requests so assert_tag works. #4810 [jarkko@jlaine.net, easleydp@gmail.com]
+* Reset @html_document between requests so assert_tag works. #4810 [Jarkko Laine, easleydp@gmail.com]
* Integration tests behave well with render_component. #4632 [edward.frederick@revolution.com, dev.rubyonrails@maxdunn.com]
@@ -2366,21 +2386,21 @@ superclass' view_paths. [Rick]
* Fixed that real files and symlinks should be treated the same when compiling templates #5438 [zachary@panandscan.com]
-* Fixed that the flash should be reset when reset_session is called #5584 [shugo@ruby-lang.org]
+* Fixed that the flash should be reset when reset_session is called #5584 [Shugo Maeda]
* Added special case for "1 Byte" in NumberHelper#number_to_human_size #5593 [murpyh@rubychan.de]
-* Fixed proper form-encoded parameter parsing for requests with "Content-Type: application/x-www-form-urlencoded; charset=utf-8" (note the presence of a charset directive) [DHH]
+* Fixed proper form-encoded parameter parsing for requests with "Content-Type: application/x-www-form-urlencoded; charset=utf-8" (note the presence of a charset directive) [David Heinemeier Hansson]
* Add route_name_path method to generate only the path for a named routes. For example, map.person will add person_path. [Nicholas Seckar]
* Avoid naming collision among compiled view methods. [Jeremy Kemper]
-* Fix CGI extensions when they expect string but get nil in Windows. Closes #5276 [mislav@nippur.irb.hr]
+* Fix CGI extensions when they expect string but get nil in Windows. Closes #5276 [Mislav Marohnić]
* Determine the correct template_root for deeply nested components. #2841 [s.brink@web.de]
-* Fix that routes with *path segments in the recall can generate URLs. [Rick]
+* Fix that routes with *path segments in the recall can generate URLs. [Rick Olson]
* Fix strip_links so that it doesn't hang on multiline <acronym> tags [Jamis Buck]
@@ -2396,11 +2416,11 @@ superclass' view_paths. [Rick]
* Rewind readable CGI params so others may reread them (such as CGI::Session when passing the session id in a multipart form). #210 [mklame@atxeu.com, matthew@walker.wattle.id.au]
-* Added Mime::TEXT (text/plain) and Mime::ICS (text/calendar) as new default types [DHH]
+* Added Mime::TEXT (text/plain) and Mime::ICS (text/calendar) as new default types [David Heinemeier Hansson]
-* Added Mime::Type.register(string, symbol, synonyms = []) for adding new custom mime types [DHH]. Example: Mime::Type.register("image/gif", :gif)
+* Added Mime::Type.register(string, symbol, synonyms = []) for adding new custom mime types [David Heinemeier Hansson]. Example: Mime::Type.register("image/gif", :gif)
-* Added support for Mime objects in render :content_type option [DHH]. Example: render :text => some_atom, :content_type => Mime::ATOM
+* Added support for Mime objects in render :content_type option [David Heinemeier Hansson]. Example: render :text => some_atom, :content_type => Mime::ATOM
* Add :status option to send_data and send_file. Defaults to '200 OK'. #5243 [Manfred Stienstra <m.stienstra@fngtps.com>]
@@ -2414,7 +2434,7 @@ superclass' view_paths. [Rick]
* Accept multipart PUT parameters. #5235 [guy.naor@famundo.com]
-* Added interrogation of params[:format] to determine Accept type. If :format is specified and matches a declared extension, like "rss" or "xml", that mime type will be put in front of the accept handler. This means you can link to the same action from different extensions and use that fact to determine output [DHH]. Example:
+* Added interrogation of params[:format] to determine Accept type. If :format is specified and matches a declared extension, like "rss" or "xml", that mime type will be put in front of the accept handler. This means you can link to the same action from different extensions and use that fact to determine output [David Heinemeier Hansson]. Example:
class WeblogController < ActionController::Base
def index
@@ -2447,13 +2467,13 @@ superclass' view_paths. [Rick]
All this relies on the fact that you have a route that includes .:format.
-* Expanded :method option in FormTagHelper#form_tag, FormHelper#form_for, PrototypeHelper#remote_form_for, PrototypeHelper#remote_form_tag, and PrototypeHelper#link_to_remote to allow for verbs other than GET and POST by automatically creating a hidden form field named _method, which will simulate the other verbs over post [DHH]
+* Expanded :method option in FormTagHelper#form_tag, FormHelper#form_for, PrototypeHelper#remote_form_for, PrototypeHelper#remote_form_tag, and PrototypeHelper#link_to_remote to allow for verbs other than GET and POST by automatically creating a hidden form field named _method, which will simulate the other verbs over post [David Heinemeier Hansson]
-* Added :method option to UrlHelper#link_to, which allows for using other verbs than GET for the link. This replaces the :post option, which is now deprecated. Example: link_to "Destroy", person_url(:id => person), :method => :delete [DHH]
+* Added :method option to UrlHelper#link_to, which allows for using other verbs than GET for the link. This replaces the :post option, which is now deprecated. Example: link_to "Destroy", person_url(:id => person), :method => :delete [David Heinemeier Hansson]
* follow_redirect doesn't complain about being redirected to the same controller. #5153 [dymo@mk.ukrtelecom.ua]
-* Add layout attribute to response object with the name of the layout that was rendered, or nil if none rendered. [Kevin Clark kevin.clark@gmail.com]
+* Add layout attribute to response object with the name of the layout that was rendered, or nil if none rendered. [Kevin Clark]
* Fix NoMethodError when parsing params like &&. [Adam Greenfield]
@@ -2461,7 +2481,7 @@ superclass' view_paths. [Rick]
* Excise ingrown code from FormOptionsHelper#options_for_select. #5008 [anonymous]
-* Small fix in routing to allow dynamic routes (broken after [4242]) [Rick]
+* Small fix in routing to allow dynamic routes (broken after [4242]) [Rick Olson]
map.connect '*path', :controller => 'files', :action => 'show'
@@ -2471,17 +2491,17 @@ superclass' view_paths. [Rick]
error_messages_for :account, :user, :subscription, :object_name => :account
-* Fix assert_redirected_to tests according to real-world usage. Also, don't fail if you add an extra :controller option: [Rick]
+* Fix assert_redirected_to tests according to real-world usage. Also, don't fail if you add an extra :controller option: [Rick Olson]
redirect_to :action => 'new'
assert_redirected_to :controller => 'monkeys', :action => 'new'
-* Diff compared routing options. Allow #assert_recognizes to take a second arg as a hash to specify optional request method [Rick]
+* Diff compared routing options. Allow #assert_recognizes to take a second arg as a hash to specify optional request method [Rick Olson]
assert_recognizes({:controller => 'users', :action => 'index'}, 'users')
assert_recognizes({:controller => 'users', :action => 'create'}, {:path => 'users', :method => :post})
-* Diff compared options with #assert_redirected_to [Rick]
+* Diff compared options with #assert_redirected_to [Rick Olson]
* Add support in routes for semicolon delimited "subpaths", like /books/:id;:action [Jamis Buck]
@@ -2502,9 +2522,9 @@ superclass' view_paths. [Rick]
*1.12.4* (August 8th, 2006)
-* Cache CgiRequest#request_parameters so that multiple calls don't re-parse multipart data. [Rick]
+* Cache CgiRequest#request_parameters so that multiple calls don't re-parse multipart data. [Rick Olson]
-* Fixed that remote_form_for can leave out the object parameter and default to the instance variable of the object_name, just like form_for [DHH]
+* Fixed that remote_form_for can leave out the object parameter and default to the instance variable of the object_name, just like form_for [David Heinemeier Hansson]
* Added ActionController.filter_parameter_logging that makes it easy to remove passwords, credit card numbers, and other sensitive information from being logged when a request is handled. #1897 [jeremye@bsa.ca.gov]
@@ -2545,7 +2565,7 @@ superclass' view_paths. [Rick]
* Update the diagnostics template skip the useless '<controller not set>' text. [Nicholas Seckar]
-* CHANGED DEFAULT: Don't parse YAML input by default, but keep it available as an easy option [DHH]
+* CHANGED DEFAULT: Don't parse YAML input by default, but keep it available as an easy option [David Heinemeier Hansson]
* Add additional autocompleter options [aballai, Thomas Fuchs]
@@ -2553,7 +2573,7 @@ superclass' view_paths. [Rick]
* Applied Prototype $() performance patches (#4465, #4477) and updated script.aculo.us [Sam Stephenson, Thomas Fuchs]
-* Added automated timestamping to AssetTagHelper methods for stylesheets, javascripts, and images when Action Controller is run under Rails [DHH]. Example:
+* Added automated timestamping to AssetTagHelper methods for stylesheets, javascripts, and images when Action Controller is run under Rails [David Heinemeier Hansson]. Example:
image_tag("rails.png") # => '<img alt="Rails" src="/images/rails.png?1143664135" />'
@@ -2587,9 +2607,9 @@ superclass' view_paths. [Rick]
* Fix double url escaping of remote_function. Add :escape => false option to ActionView's url_for. [Nicholas Seckar]
-* Add :script option to in_place_editor to support evalScripts (closes #4194) [codyfauser@gmail.com]
+* Add :script option to in_place_editor to support evalScripts (closes #4194) [Cody Fauser]
-* Fix mixed case enumerable methods in the JavaScript Collection Proxy (closes #4314) [codyfauser@gmail.com]
+* Fix mixed case enumerable methods in the JavaScript Collection Proxy (closes #4314) [Cody Fauser]
* Undo accidental escaping for mail_to; add regression test. [Nicholas Seckar]
@@ -2624,9 +2644,9 @@ superclass' view_paths. [Rick]
* XML-formatted requests are typecast according to "type" attributes for :xml_simple [Jamis Buck]
-* Added protection against proxy setups treating requests as local even when they're not #3898 [stephen_purcell@yahoo.com]
+* Added protection against proxy setups treating requests as local even when they're not #3898 [Steve Purcell]
-* Added TestRequest#raw_post that simulate raw_post from CgiRequest #3042 [francois.beausoleil@gmail.com]
+* Added TestRequest#raw_post that simulate raw_post from CgiRequest #3042 [François Beausoleil]
* Underscore dasherized keys in formatted requests [Jamis Buck]
@@ -2642,11 +2662,11 @@ superclass' view_paths. [Rick]
* Added simple alert() notifications for RJS exceptions when config.action_view.debug_rjs = true. [Sam Stephenson]
-* Added :content_type option to render, so you can change the content type on the fly [DHH]. Example: render :action => "atom.rxml", :content_type => "application/atom+xml"
+* Added :content_type option to render, so you can change the content type on the fly [David Heinemeier Hansson]. Example: render :action => "atom.rxml", :content_type => "application/atom+xml"
-* CHANGED DEFAULT: The default content type for .rxml is now application/xml instead of type/xml, see http://www.xml.com/pub/a/2004/07/21/dive.html for reason [DHH]
+* CHANGED DEFAULT: The default content type for .rxml is now application/xml instead of type/xml, see http://www.xml.com/pub/a/2004/07/21/dive.html for reason [David Heinemeier Hansson]
-* Added option to render action/template/file of a specific extension (and here by template type). This means you can have multiple templates with the same name but a different extension [DHH]. Example:
+* Added option to render action/template/file of a specific extension (and here by template type). This means you can have multiple templates with the same name but a different extension [David Heinemeier Hansson]. Example:
class WeblogController < ActionController::Base
def index
@@ -2660,7 +2680,7 @@ superclass' view_paths. [Rick]
end
end
-* Added better support for using the same actions to output for different sources depending on the Accept header [DHH]. Example:
+* Added better support for using the same actions to output for different sources depending on the Accept header [David Heinemeier Hansson]. Example:
class WeblogController < ActionController::Base
def create
@@ -2677,9 +2697,9 @@ superclass' view_paths. [Rick]
end
end
-* Added Base#render(:xml => xml) that works just like Base#render(:text => text), but sets the content-type to text/xml and the charset to UTF-8 [DHH]
+* Added Base#render(:xml => xml) that works just like Base#render(:text => text), but sets the content-type to text/xml and the charset to UTF-8 [David Heinemeier Hansson]
-* Integration test's url_for now runs in the context of the last request (if any) so after post /products/show/1 url_for :action => 'new' will yield /product/new [Tobias Luetke]
+* Integration test's url_for now runs in the context of the last request (if any) so after post /products/show/1 url_for :action => 'new' will yield /product/new [Tobias Lütke]
* Re-added mixed-in helper methods for the JavascriptGenerator. Moved JavascriptGenerators methods to a module that is mixed in after the helpers are added. Also fixed that variables set in the enumeration methods like #collect are set correctly. Documentation added for the enumeration methods [Rick Olson]. Examples:
@@ -2699,7 +2719,7 @@ superclass' view_paths. [Rick]
# Assign the default XmlSimple to a new content type
ActionController::Base.param_parsers['application/backpack+xml'] = :xml_simple
-Default YAML web services were retired, ActionController::Base.param_parsers carries an example which shows how to get this functionality back. As part of this new plugin support, request.[formatted_post?, xml_post?, yaml_post? and post_format] were all deprecated in favor of request.content_type [Tobias Luetke]
+Default YAML web services were retired, ActionController::Base.param_parsers carries an example which shows how to get this functionality back. As part of this new plugin support, request.[formatted_post?, xml_post?, yaml_post? and post_format] were all deprecated in favor of request.content_type [Tobias Lütke]
* Fixed Effect.Appear in effects.js to work with floats in Safari #3524, #3813, #3044 [Thomas Fuchs]
@@ -2729,11 +2749,11 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added various InPlaceEditor options, #3746, #3891, #3896, #3906 [Bill Burcham, ruairi, sl33p3r]
-* Added :count option to pagination that'll make it possible for the ActiveRecord::Base.count call to using something else than * for the count. Especially important for count queries using DISTINCT #3839 [skaes]
+* Added :count option to pagination that'll make it possible for the ActiveRecord::Base.count call to using something else than * for the count. Especially important for count queries using DISTINCT #3839 [Stefan Kaes]
* Update script.aculo.us to V1.5.2 [Thomas Fuchs]
-* Added element and collection proxies to RJS [DHH]. Examples:
+* Added element and collection proxies to RJS [David Heinemeier Hansson]. Examples:
page['blank_slate'] # => $('blank_slate');
page['blank_slate'].show # => $('blank_slate').show();
@@ -2779,7 +2799,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Document the :xhr option for verifications. #3666 [leeo]
-* Added :only and :except controls to skip_before/after_filter just like for when you add filters [DHH]
+* Added :only and :except controls to skip_before/after_filter just like for when you add filters [David Heinemeier Hansson]
* Ensure that the instance variables are copied to the template when performing render :update. [Nicholas Seckar]
@@ -2801,13 +2821,13 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Fix render(:update) to not render layouts. [Sam Stephenson]
-* Fixed that SSL would not correctly be detected when running lighttpd/fcgi behind lighttpd w/mod_proxy #3548 [stephen_purcell@yahoo.com]
+* Fixed that SSL would not correctly be detected when running lighttpd/fcgi behind lighttpd w/mod_proxy #3548 [Steve Purcell]
* Added the possibility to specify atomatic expiration for the memcachd session container #3571 [Stefan Kaes]
* Change layout discovery to take into account the change in semantics with File.join and nil arguments. [Marcel Molina Jr.]
-* Raise a RedirectBackError if redirect_to :back is called when there's no HTTP_REFERER defined #3049 [kevin.clark@gmail.com]
+* Raise a RedirectBackError if redirect_to :back is called when there's no HTTP_REFERER defined #3049 [Kevin Clark]
* Treat timestamps like datetimes for scaffolding purposes #3388 [Maik Schmidt]
@@ -2847,15 +2867,15 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Have the form builder output radio button, not check box, when calling the radio button helper. #3331 [LouisStAmour@gmail.com]
-* Added assignment of the Autocompleter object created by JavaScriptMacroHelper#auto_complete_field to a local javascript variables [DHH]
+* Added assignment of the Autocompleter object created by JavaScriptMacroHelper#auto_complete_field to a local javascript variables [David Heinemeier Hansson]
-* Added :on option for PrototypeHelper#observe_field that allows you to specify a different callback hook to have the observer trigger on [DHH]
+* Added :on option for PrototypeHelper#observe_field that allows you to specify a different callback hook to have the observer trigger on [David Heinemeier Hansson]
-* Added JavaScriptHelper#button_to_function that works just like JavaScriptHelper#link_to_function but uses a button instead of a href [DHH]
+* Added JavaScriptHelper#button_to_function that works just like JavaScriptHelper#link_to_function but uses a button instead of a href [David Heinemeier Hansson]
-* Added that JavaScriptHelper#link_to_function will honor existing :onclick definitions when adding the function call [DHH]
+* Added that JavaScriptHelper#link_to_function will honor existing :onclick definitions when adding the function call [David Heinemeier Hansson]
-* Added :disable_with option to FormTagHelper#submit_tag to allow for easily disabled submit buttons with different text [DHH]
+* Added :disable_with option to FormTagHelper#submit_tag to allow for easily disabled submit buttons with different text [David Heinemeier Hansson]
* Make auto_link handle nil by returning quickly if blank? [Scott Barron]
@@ -2873,7 +2893,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
page.visual_effect :fade, 'notice'
end
-* Add session ID to default logging, but remove the verbose description of every step [DHH]
+* Add session ID to default logging, but remove the verbose description of every step [David Heinemeier Hansson]
* Add the following RJS methods: [Sam Stephenson]
@@ -2895,7 +2915,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Update to Prototype 1.4.0 final [Sam Stephenson]
-* Added form_remote_for (form_for meets form_remote_tag) [DHH]
+* Added form_remote_for (form_for meets form_remote_tag) [David Heinemeier Hansson]
* Update to script.aculo.us 1.5.0_rc6
@@ -2903,7 +2923,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Fix docs for text_area_tag. #3083. [Christopher Cotton]
-* Change form_for and fields_for method signatures to take object name and object as separate arguments rather than as a Hash. [DHH]
+* Change form_for and fields_for method signatures to take object name and object as separate arguments rather than as a Hash. [David Heinemeier Hansson]
* Introduce :selected option to the select helper. Allows you to specify a selection other than the current value of object.method. Specify :selected => nil to leave all options unselected. #2991 [Jonathan Viney <jonathan@bluewire.net.nz>]
@@ -2921,7 +2941,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Pass multiple arguments to Element.show and Element.hide in JavaScriptGenerator instead of using iterators. [Sam Stephenson]
-* Improve expire_fragment documentation. #2966 [court3nay@gmail.com]
+* Improve expire_fragment documentation. #2966 [court3nay]
* Correct docs for automatic layout assignment. #2610. [Charles M. Gerungan]
@@ -2943,7 +2963,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Correct time_zone_options_for_select docs. #2892 [pudeyo@rpi.com]
-* Remove the unused, slow response_dump and session_dump variables from error pages. #1222 [lmarlow@yahoo.com]
+* Remove the unused, slow response_dump and session_dump variables from error pages. #1222 [lmarlow]
* Performance tweaks: use Set instead of Array to speed up prototype helper include? calls. Avoid logging code if logger is nil. Inline commonly-called template presence checks. #2880, #2881, #2882, #2883 [Stefan Kaes]
@@ -2973,9 +2993,9 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Update documentation for render :file. #2858 [Tom Werner]
-* Only include builtin filters whose filenames match /^[a-z][a-z_]*_helper.rb$/ to avoid including operating system metadata such as ._foo_helper.rb. #2855 [court3nay@gmail.com]
+* Only include builtin filters whose filenames match /^[a-z][a-z_]*_helper.rb$/ to avoid including operating system metadata such as ._foo_helper.rb. #2855 [court3nay]
-* Added FormHelper#form_for and FormHelper#fields_for that makes it easier to work with forms for single objects also if they don't reside in instance variables [DHH]. Examples:
+* Added FormHelper#form_for and FormHelper#fields_for that makes it easier to work with forms for single objects also if they don't reside in instance variables [David Heinemeier Hansson]. Examples:
<% form_for :person, @person, :url => { :action => "update" } do |f| %>
First name: <%= f.text_field :first_name %>
@@ -3008,7 +3028,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
*1.11.0* (November 7th, 2005)
-* Added request as instance method to views, so you can do <%= request.env["HTTP_REFERER"] %>, just like you can already access response, session, and the likes [DHH]
+* Added request as instance method to views, so you can do <%= request.env["HTTP_REFERER"] %>, just like you can already access response, session, and the likes [David Heinemeier Hansson]
* Fix conflict with assert_tag and Glue gem #2255 [david.felstead@gmail.com]
@@ -3016,9 +3036,9 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added action_pack.rb stub so that ActionPack::Version loads properly [Sam Stephenson]
-* Added short-hand to assert_tag so assert_tag :tag => "span" can be written as assert_tag "span" [DHH]
+* Added short-hand to assert_tag so assert_tag :tag => "span" can be written as assert_tag "span" [David Heinemeier Hansson]
-* Added skip_before_filter/skip_after_filter for easier control of the filter chain in inheritance hierachies [DHH]. Example:
+* Added skip_before_filter/skip_after_filter for easier control of the filter chain in inheritance hierachies [David Heinemeier Hansson]. Example:
class ApplicationController < ActionController::Base
before_filter :authenticate
@@ -3033,7 +3053,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
skip_before_filter :authenticate
end
-* Added redirect_to :back as a short-hand for redirect_to(request.env["HTTP_REFERER"]) [DHH]
+* Added redirect_to :back as a short-hand for redirect_to(request.env["HTTP_REFERER"]) [David Heinemeier Hansson]
* Change javascript_include_tag :defaults to not use script.aculo.us loader, which facilitates the use of plugins for future script.aculo.us and third party javascript extensions, and provide register_javascript_include_default for plugins to specify additional JavaScript files to load. Removed slider.js and builder.js from actionpack. [Thomas Fuchs]
@@ -3043,7 +3063,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Update script.aculo.us to V1.5_rc4 [Thomas Fuchs]
-* Fix that render :text didn't interpolate instance variables #2629, #2626 [skaes]
+* Fix that render :text didn't interpolate instance variables #2629, #2626 [Stefan Kaes]
* Fix line number detection and escape RAILS_ROOT in backtrace Regexp [Nicholas Seckar]
@@ -3062,7 +3082,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
*1.10.2* (October 26th, 2005)
-* Reset template variables after using render_to_string [skaes@web.de]
+* Reset template variables after using render_to_string [Stefan Kaes]
* Expose the session model backing CGI::Session
@@ -3078,7 +3098,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
*1.10.0* (October 16th, 2005)
-* Make string-keys locals assigns optional. Add documentation describing depreciated state [skaes@web.de]
+* Make string-keys locals assigns optional. Add documentation describing depreciated state [Stefan Kaes]
* Improve line number detection for template errors [Nicholas Seckar]
@@ -3086,7 +3106,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Upgrade to Prototype 1.4.0_rc0 [Sam Stephenson]
-* Added assert_vaild. Reports the proper AR error messages as fail message when the passed record is invalid [Tobias Luetke]
+* Added assert_vaild. Reports the proper AR error messages as fail message when the passed record is invalid [Tobias Lütke]
* Add temporary support for passing locals to render using string keys [Nicholas Seckar]
@@ -3094,11 +3114,11 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Raise an exception if an attempt is made to insert more session data into the ActiveRecordStore data column than the column can hold. #2234. [justin@textdrive.com]
-* Removed references to assertions.rb from actionpack assert's backtraces. Makes error reports in functional unit tests much less noisy. [Tobias Luetke]
+* Removed references to assertions.rb from actionpack assert's backtraces. Makes error reports in functional unit tests much less noisy. [Tobias Lütke]
* Updated and clarified documentation for JavaScriptHelper to be more concise about the various options for including the JavaScript libs. [Thomas Fuchs]
-* Hide "Retry with Breakpoint" button on error pages until feature is functional. [DHH]
+* Hide "Retry with Breakpoint" button on error pages until feature is functional. [David Heinemeier Hansson]
* Fix Request#host_with_port to use the standard port when Rails is behind a proxy. [Nicholas Seckar]
@@ -3116,7 +3136,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Misc doc fixes (typos/grammar/etc). #2445. [coffee2code]
-* Speed improvement for session_options. #2287. [skaes@web.de]
+* Speed improvement for session_options. #2287. [Stefan Kaes]
* Make cacheing binary files friendly with Windows. #1975. [Rich Olson]
@@ -3128,9 +3148,9 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Make assert_tag :children count appropriately. Closes #2181. [jamie@bravenet.com]
-* Forced newer versions of RedCloth to use hard breaks [DHH]
+* Forced newer versions of RedCloth to use hard breaks [David Heinemeier Hansson]
-* Added new scriptaculous options for auto_complete_field #2343 [m.stienstra@fngtps.com]
+* Added new scriptaculous options for auto_complete_field #2343 [Manfred Stienstra]
* Don't prepend the asset host if the string is already a fully-qualified URL
@@ -3144,13 +3164,13 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Keep flash after components are rendered. #2291 [Rick Olson, Scott]
-* Shorten IE file upload path to filename only to match other browsers. #1507 [court3nay@gmail.com]
+* Shorten IE file upload path to filename only to match other browsers. #1507 [court3nay]
* Fix open/save dialog in IE not opening files send with send_file/send_data, #2279 [Thomas Fuchs]
-* Fixed that auto_discovery_link_tag couldn't take a string as the URL [DHH]
+* Fixed that auto_discovery_link_tag couldn't take a string as the URL [David Heinemeier Hansson]
-* Fixed problem with send_file and WEBrick using stdout #1812 [DHH]
+* Fixed problem with send_file and WEBrick using stdout #1812 [David Heinemeier Hansson]
* Optimized tag_options to not sort keys, which is no longer necessary when assert_dom_equal and friend is available #1995 [skae]
@@ -3168,7 +3188,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Speed up cookie use by decreasing string copying #2194 [skae]
-* Fixed access to "Host" header with requests made by crappy old HTTP/1.0 clients #2124 [Marcel Molina]
+* Fixed access to "Host" header with requests made by crappy old HTTP/1.0 clients #2124 [Marcel Molina Jr.]
* Added easy assignment of fragment cache store through use of symbols for included stores (old way still works too)
@@ -3218,7 +3238,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Avoid extending view instance with helper modules each request. Closes #1979
-* Performance improvements to CGI methods. Closes #1980 [Skaes]
+* Performance improvements to CGI methods. Closes #1980 [Stefan Kaes]
* Added :post option to UrlHelper#link_to that makes it possible to do POST requests through normal ahref links using Javascript
@@ -3235,13 +3255,13 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Drop trailing \000 if present on RAW_POST_DATA (works around bug in Safari Ajax implementation) #918
-* Fix observe_field to fall back to event-based observation if frequency <= 0 #1916 [michael@schubert.cx]
+* Fix observe_field to fall back to event-based observation if frequency <= 0 #1916 [Michael Schubert]
* Allow use of the :with option for submit_to_remote #1936 [jon@instance-design.co.uk]
-* AbstractRequest#domain returns nil when host is an ip address #2012 [kevin.clark@gmail.com]
+* AbstractRequest#domain returns nil when host is an ip address #2012 [Kevin Clark]
-* ActionController documentation update #2051 [fbeausoleil@ftml.net]
+* ActionController documentation update #2051 [François Beausoleil]
* Yield @content_for_ variables to templates #2058 [Sam Stephenson]
@@ -3257,7 +3277,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Fix Routing to handle :some_param => nil better. [Nicholas Seckar, Luminas]
-* Add support for :include with pagination (subject to existing constraints for :include with :limit and :offset) #1478 [michael@schubert.cx]
+* Add support for :include with pagination (subject to existing constraints for :include with :limit and :offset) #1478 [Michael Schubert]
* Prevent the benchmark module from blowing up if a non-HTTP/1.1 request is processed
@@ -3285,7 +3305,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added support for per-action session management #1763
-* Improved rendering speed on complicated templates by up to 100% (the more complex the templates, the higher the speedup) #1234 [Stephan Kaes]. This did necessasitate a change to the internals of ActionView#render_template that now has four parameters. Developers of custom view handlers (like Amrita) need to update for that.
+* Improved rendering speed on complicated templates by up to 100% (the more complex the templates, the higher the speedup) #1234 [Stefan Kaes]. This did necessasitate a change to the internals of ActionView#render_template that now has four parameters. Developers of custom view handlers (like Amrita) need to update for that.
* Added options hash as third argument to FormHelper#input, so you can do input('person', 'zip', :size=>10) #1719 [jeremye@bsa.ca.gov]
@@ -3312,7 +3332,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Fixed that Action View should always use the included Builder, never attempt to require the gem, to ensure compatibility
-* Added that nil options are not included in tags, so tag("p", :ignore => nil) now returns <p /> not <p ignore="" /> but that tag("p", :ignore => "") still includes it #1465 [michael@schuerig.de]
+* Added that nil options are not included in tags, so tag("p", :ignore => nil) now returns <p /> not <p ignore="" /> but that tag("p", :ignore => "") still includes it #1465 [Michael Schuerig]
* Fixed that UrlHelper#link_to_unless/link_to_if used html_escape on the name if no link was to be applied. This is unnecessary and breaks its use with images #1649 [joergd@pobox.com]
@@ -3350,9 +3370,9 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added javascript_include_tag :defaults shortcut that'll include all the default javascripts included with Action Pack (prototype, effects, controls, dragdrop)
-* Cache several controller variables that are expensive to calculate #1229 [skaes@web.de]
+* Cache several controller variables that are expensive to calculate #1229 [Stefan Kaes]
-* The session class backing CGI::Session::ActiveRecordStore may be replaced with any class that duck-types with a subset of Active Record. See docs for details #1238 [skaes@web.de]
+* The session class backing CGI::Session::ActiveRecordStore may be replaced with any class that duck-types with a subset of Active Record. See docs for details #1238 [Stefan Kaes]
* Fixed that hashes was not working properly when passed by GET to lighttpd #849 [Nicholas Seckar]
@@ -3369,7 +3389,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
<p>Product 2</p>
<% end %>
-* Added :field_name option to DateHelper#select_(year|month|day) to deviate from the year/month/day defaults #1266 [Marcel Molina]
+* Added :field_name option to DateHelper#select_(year|month|day) to deviate from the year/month/day defaults #1266 [Marcel Molina Jr.]
* Added JavascriptHelper#draggable_element and JavascriptHelper#drop_receiving_element to facilitate easy dragging and dropping through the script.aculo.us libraries #1578 [Thomas Fuchs]
@@ -3413,7 +3433,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added named routes (NEEDS BETTER DESCRIPTION) #1434 [Nicholas Seckar]
-* Improved AbstractRequest documentation #1483 [court3nay@gmail.com]
+* Improved AbstractRequest documentation #1483 [court3nay]
* Added ActionController::Base.allow_concurrency to control whether the application is thread-safe, so multi-threaded servers like WEBrick knows whether to apply a mutex around the performance of each action. Turned off by default. EXPERIMENTAL FEATURE.
@@ -3425,7 +3445,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added :xhr => true/false option to verify so you can ensure that a request is coming from an Ajax call or not #1464 [Thomas Fuchs]
-* Added tag_options as a third parameter to AssetHelper#auto_discovery_link_tag to control options like the title of the link #1430 [kevin.clark@gmail.com]
+* Added tag_options as a third parameter to AssetHelper#auto_discovery_link_tag to control options like the title of the link #1430 [Kevin Clark]
* Added option to pass in parameters to CaptureHelper#capture, so you can create more advanced view helper methods #1466 [duane.johnson@gmail.com]. Example:
@@ -3451,11 +3471,11 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added button_to as a form-based solution to deal with harmful actions that should be hidden behind POSTs. This makes it just as easy as link_to to create a safe trigger for actions like destroy, although it's limited by being a block element, the fixed look, and a no-no inside other forms. #1371 [tom@moertel.com]
-* Fixed image_tag so an exception is not thrown just because the image is missing and alt value can't be generated #1395 [Marcel]
+* Fixed image_tag so an exception is not thrown just because the image is missing and alt value can't be generated #1395 [Marcel Molina Jr.]
* Added a third parameter to TextHelper#auto_link called href_options for specifying additional tag options on the links generated #1401 [tyler.kovacs@gmail.com]. Example: auto_link(text, :all, { :target => "_blank" }) to have all the generated links open in a new window.
-* Fixed TextHelper#highlight to return the text, not nil, if the phrase is blank #1409 [patrick@lenz.sh]
+* Fixed TextHelper#highlight to return the text, not nil, if the phrase is blank #1409 [Patrick Lenz]
* Fixed TagHelper such that :name and 'name' keys in the options doesn't result in two attributes #1455 [take_tk]
@@ -3469,7 +3489,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Allow distance_of_time_in_words to work with any value that responds to #to_time (like dates) #969
-* Support :render option for :verify #1440 [TobiasLuetke]
+* Support :render option for :verify #1440 [Tobias Lütke]
* Updated vendor copy of html-scanner lib to 0.5.2, for bug fixes and optimizations. The :content option may be used as expected--to find a tag whose textual content is a particular value--in assert_tag, now.
@@ -3583,7 +3603,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Fixed action/fragment caching using the filestore when a directory and a file wanted to use the same name. Now there's a .cache prefix that sidesteps the conflict #1188 [imbcmdth@hotmail.com]
-* Fixed missing id uniqueness in FormTag#radio_button #1207 [Jarkko]
+* Fixed missing id uniqueness in FormTag#radio_button #1207 [Jarkko Laine]
* Fixed assert_redirected_to to work with :only_path => false #1204 [Alisdair McDiarmid]
@@ -3688,7 +3708,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added support for web servers that use PATH_INFO instead of REQUEST_URI like IIS #1014 [BradG/Nicholas Seckar]
-* Added graceful handling of PUT, DELETE, and OPTIONS requests for a complete coverage of REST functionality #1136 [joshknowles@gmail.com]
+* Added graceful handling of PUT, DELETE, and OPTIONS requests for a complete coverage of REST functionality #1136 [Josh Knowles]
* Fixed that you can now pass an alternative :href option to link_to_function/remote in order to point to somewhere other than # if the javascript fails or is turned off. You can do the same with form_remote_tag by passing in :action. #1113 [Sam Stephenson]
@@ -3712,7 +3732,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Fixed pagination to work with joins #1034 [scott@sigkill.org]
-* Fixed that *rest parameter in map.connect couldn't accept an empty list #1037 [Dee.Zsombor@gmail.com]
+* Fixed that *rest parameter in map.connect couldn't accept an empty list #1037 [Dee Zsombor]
* Added :confirm option to link_to_remote just like link_to has #1082 [yrashk@fp.org.ua]
@@ -3732,7 +3752,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added JavascriptHelper#periodically_call_remote in order to create areas of a page that update automatically at a set interval #945 [Jon Tirsen]
-* Fixed Cache#expire_matched_fragments that couldn't recognize the difference between string and url_for options #1030 [skaes@web.de]
+* Fixed Cache#expire_matched_fragments that couldn't recognize the difference between string and url_for options #1030 [Stefan Kaes]
* Added simulation of @request.request_uri in functional tests #1038 [Jamis Buck]
@@ -3775,7 +3795,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added :position option to link_to_remote/form_remote_tag that can be either :before, :top, :bottom, or :after and specifies where the return from the method should be inserted #952 [Matthew McCray/Sam Stephenson]
-* Added Effect.Highlight to prototype.js to do Yellow Fade Technique (of 37signals' fame) on any container #952 [Sam Stephenson/courtenay]
+* Added Effect.Highlight to prototype.js to do Yellow Fade Technique (of 37signals' fame) on any container #952 [Sam Stephenson/court3nay]
* Added include_seconds option as the third parameter to distance_of_time_in_words which will render "less than a minute" in higher resolution ("less than 10 seconds" etc) #944 [thomas@fesch.at]
@@ -3789,9 +3809,9 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added trailing_slash option to url_for, so you can generate urls ending in a slash. Note that is currently not recommended unless you need it for special reasons since it breaks caching #937 [stian@grytoyr.net]
-* Added expire_matched_fragments(regular_expression) to clear out a lot of fragment caches at once #927 [technoweenie@gmail.com]
+* Added expire_matched_fragments(regular_expression) to clear out a lot of fragment caches at once #927 [Rick Olson]
-* Fixed the problems with : and ? in file names for fragment caches on Windows #927 [technoweenie@gmail.com]
+* Fixed the problems with : and ? in file names for fragment caches on Windows #927 [Rick Olson]
* Added TextHelper#human_size for formatting file sizes, like human_size(1234567) => 1.2 MB #943 [thomas@fesch.at]
@@ -3808,7 +3828,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added pagination support through both a controller and helper add-on #817 [Sam Stephenson]
-* Fixed routing and helpers to make Rails work on non-vhost setups #826 [Nicholas Seckar/Tobias Luetke]
+* Fixed routing and helpers to make Rails work on non-vhost setups #826 [Nicholas Seckar/Tobias Lütke]
* Added a much improved Flash module that allows for finer-grained control on expiration and allows you to flash the current action #839 [Caio Chassot]. Example of flash.now:
@@ -3867,7 +3887,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Fixed options_for_select on selected line issue #624 [Florian Weber]
-* Added CaptureHelper with CaptureHelper#capture and CaptureHelper#content_for. See documentation in helper #837 [Tobias Luetke]
+* Added CaptureHelper with CaptureHelper#capture and CaptureHelper#content_for. See documentation in helper #837 [Tobias Lütke]
* Fixed :anchor use in url_for #821 [Nicholas Seckar]
@@ -4003,7 +4023,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Fixed the ordering of attributes in the xml-decleration of Builder #540 [woeye]
-* Added @request.raw_post as a convenience access to @request.env['RAW_POST_DATA'] #534 [Tobias Luetke]
+* Added @request.raw_post as a convenience access to @request.env['RAW_POST_DATA'] #534 [Tobias Lütke]
* Added support for automatic id-based indexing for lists of items #532 [dblack]. Example:
@@ -4093,13 +4113,13 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Fixed url rewriter confusion when the controller or action name was a substring of the controller_prefix or action_prefix
-* Added conditional layouts like <tt>layout "weblog_standard", :except => :rss</tt> #452 [Marcel Molina]
+* Added conditional layouts like <tt>layout "weblog_standard", :except => :rss</tt> #452 [Marcel Molina Jr.]
* Fixed that MemCacheStore wasn't included by default and added default MemCache object pointing to localhost #447 [Lucas Carlson]
-* Added fourth argument to render_collection_of_partials that allows you to specify local_assigns -- just like render_partial #432 [zenspider]
+* Added fourth argument to render_collection_of_partials that allows you to specify local_assigns -- just like render_partial #432 [Ryan Davis]
-* Fixed that host would choke when cgi.host returned nil #432 [Tobias Luetke]
+* Fixed that host would choke when cgi.host returned nil #432 [Tobias Lütke]
* Added that form helpers now take an index option #448 [Tim Bates]
@@ -4131,7 +4151,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
redirect_to(:controller => "account", :action => "login") unless @session[:authenticated]
end
-* Added conditional filters #431 [Marcel]. Example:
+* Added conditional filters #431 [Marcel Molina Jr.]. Example:
class JournalController < ActionController::Base
# only require authentication if the current action is edit or delete
@@ -4165,7 +4185,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added second boolean parameter to Base.redirect_to_url and Response#redirect to control whether the redirect is permanent or not (301 vs 302) #375 [Hodel]
-* Fixed redirects when the controller and action is named the same. Still haven't fixed same controller, module, and action, though #201 [Josh]
+* Fixed redirects when the controller and action is named the same. Still haven't fixed same controller, module, and action, though #201 [Josh Peek]
* Fixed problems with running multiple functional tests in Rails under 1.8.2 by including hack for test/unit weirdness
@@ -4175,7 +4195,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
*1.1.0*
* Added search through session to clear out association caches at the end of each request. This makes it possible to place Active Record objects
- in the session without worrying about stale data in the associations (the main object is still subject to caching, naturally) #347 [Tobias Luetke]
+ in the session without worrying about stale data in the associations (the main object is still subject to caching, naturally) #347 [Tobias Lütke]
* Added more informative exception when using helper :some_helper and the helper requires another file that fails, you'll get an
error message tells you what file actually failed to load, rather than falling back on assuming it was the helper file itself #346 [dblack]
@@ -4195,7 +4215,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Fixed that textilize and markdown would instantiate their engines even on empty strings. This also fixes #333 [Ulysses]
-* Fixed UrlHelper#link_to_unless so it doesn't care if the id is a string or fixnum [zenspider]
+* Fixed UrlHelper#link_to_unless so it doesn't care if the id is a string or fixnum [Ryan Davis]
*1.0.1*
@@ -4212,7 +4232,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Fixed DateHelper#date_select so that you can pass include_blank as an option even if you don't use start_year and end_year #59 [what-a-day]
* Added that controllers will now search for a layout in $template_root/layouts/$controller_name.r(html|xml), so PostsController will look
- for layouts/posts.rhtml or layouts/posts.rxml and automatically configure this layout if found #307 [Marcel]
+ for layouts/posts.rhtml or layouts/posts.rxml and automatically configure this layout if found #307 [Marcel Molina Jr.]
* Added FormHelper#radio_button to work with radio buttons like its already possible with check boxes [Michael Koziarski]
@@ -4257,11 +4277,11 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added that render_partial will always by default include a counter with value 1 unless there is a counter passed in via the
local_assigns hash that overrides it. As a result, render_collection_of_partials can still be written in terms of render_partial
and partials that make use of a counter can be called without problems from both render_collection_of_partials as well as
- render_partial #295 [marcel]
+ render_partial #295 [Marcel Molina Jr.]
* Fixed CgiRequest#out to fall back to #write if $stdout doesn't have #syswrite [Jeremy Kemper]
-* Fixed all helpers so that they use XHTML compliant double quotes for values instead of single quotes [htonl/bitsweat]
+* Fixed all helpers so that they use XHTML compliant double quotes for values instead of single quotes [htonl/Jeremy Kemper]
* Added link_to_image(src, options = {}, html_options = {}). Documentation:
@@ -4281,11 +4301,11 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Fixed to_input_field_tag so it no longer explicitly uses InstanceTag.value if value was specified in the options hash [evl]
-* Added the possibility of having validate be protected for assert_(in)valid_column #263 [Tobias Luetke]
+* Added the possibility of having validate be protected for assert_(in)valid_column #263 [Tobias Lütke]
* Added that ActiveRecordHelper#form now calls url_for on the :action option.
-* Added all the HTTP methods as alternatives to the generic "process" for functional testing #276 [Tobias Luetke]. Examples:
+* Added all the HTTP methods as alternatives to the generic "process" for functional testing #276 [Tobias Lütke]. Examples:
# Calls Controller#miletone with a GET request
process :milestone
@@ -4442,9 +4462,9 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added a hidden field to checkboxes generated with FormHelper#check_box that will make sure that the unchecked value (usually 0)
is sent even if the checkbox is not checked. This relieves the controller from doing custom checking if the checkbox wasn't
- checked. BEWARE: This might conflict with your run-on-the-mill work-around code. [Tobias Luetke]
+ checked. BEWARE: This might conflict with your run-on-the-mill work-around code. [Tobias Lütke]
-* Fixed error_message_on to just use the first if more than one error had been added [marcel]
+* Fixed error_message_on to just use the first if more than one error had been added [Marcel Molina Jr.]
* Fixed that URL rewriting with /controller/ was working but /controller was not and that you couldn't use :id on index [geech]
@@ -4564,7 +4584,7 @@ Default YAML web services were retired, ActionController::Base.param_parsers car
* Added caching for compiled ERb templates. On Basecamp, it gave between 8.5% and 71% increase in performance [Andreas Schwarz].
-* Added implicit counter variable to render_collection_of_partials [Marcel]. From the docs:
+* Added implicit counter variable to render_collection_of_partials [Marcel Molina Jr.]. From the docs:
<%= render_collection_of_partials "ad", @advertisements %>
diff --git a/actionpack/Rakefile b/actionpack/Rakefile
index 1a1b908122..c389e5a8d6 100644
--- a/actionpack/Rakefile
+++ b/actionpack/Rakefile
@@ -81,7 +81,7 @@ spec = Gem::Specification.new do |s|
s.requirements << 'none'
s.add_dependency('activesupport', '= 2.3.0' + PKG_BUILD)
- s.add_dependency('rack', '= 0.4.0')
+ s.add_dependency('rack', '>= 0.9.0')
s.require_path = 'lib'
s.autorequire = 'action_controller'
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 5d5d6b8c9c..8c022e5625 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -31,18 +31,17 @@ rescue LoadError
end
end
-gem 'rack', '~> 0.4.0'
+gem 'rack', '>= 0.9.0'
require 'rack'
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
# the initilizer if they are really needed.
def self.load_all!
- [Base, CgiRequest, CgiResponse, RackRequest, RackRequest, Http::Headers, UrlRewriter, UrlWriter]
+ [Base, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter]
end
autoload :AbstractRequest, 'action_controller/request'
- autoload :AbstractResponse, 'action_controller/response'
autoload :Base, 'action_controller/base'
autoload :Benchmarking, 'action_controller/benchmarking'
autoload :Caching, 'action_controller/caching'
@@ -56,12 +55,17 @@ module ActionController
autoload :Integration, 'action_controller/integration'
autoload :IntegrationTest, 'action_controller/integration'
autoload :Layout, 'action_controller/layout'
+ autoload :Lock, 'action_controller/lock'
autoload :MiddlewareStack, 'action_controller/middleware_stack'
autoload :MimeResponds, 'action_controller/mime_responds'
autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes'
- autoload :RackRequest, 'action_controller/rack_process'
- autoload :RackResponse, 'action_controller/rack_process'
+ autoload :Request, 'action_controller/request'
+ autoload :RequestParser, 'action_controller/request_parser'
+ autoload :UrlEncodedPairParser, 'action_controller/url_encoded_pair_parser'
+ autoload :UploadedStringIO, 'action_controller/uploaded_file'
+ autoload :UploadedTempfile, 'action_controller/uploaded_file'
autoload :RecordIdentifier, 'action_controller/record_identifier'
+ autoload :Response, 'action_controller/response'
autoload :RequestForgeryProtection, 'action_controller/request_forgery_protection'
autoload :Rescue, 'action_controller/rescue'
autoload :Resources, 'action_controller/resources'
@@ -74,6 +78,7 @@ module ActionController
autoload :Translation, 'action_controller/translation'
autoload :UrlRewriter, 'action_controller/url_rewriter'
autoload :UrlWriter, 'action_controller/url_rewriter'
+ autoload :VerbPiggybacking, 'action_controller/verb_piggybacking'
autoload :Verification, 'action_controller/verification'
module Assertions
@@ -89,19 +94,15 @@ module ActionController
autoload :Headers, 'action_controller/headers'
end
- # DEPRECATE: Remove CGI support
- autoload :CgiRequest, 'action_controller/cgi_process'
- autoload :CgiResponse, 'action_controller/cgi_process'
- autoload :CGIHandler, 'action_controller/cgi_process'
-end
-
-class CGI
- class Session
- autoload :ActiveRecordStore, 'action_controller/session/active_record_store'
+ module Session
+ autoload :AbstractStore, 'action_controller/session/abstract_store'
autoload :CookieStore, 'action_controller/session/cookie_store'
- autoload :DRbStore, 'action_controller/session/drb_store'
autoload :MemCacheStore, 'action_controller/session/mem_cache_store'
end
+
+ # DEPRECATE: Remove CGI support
+ autoload :CgiRequest, 'action_controller/cgi_process'
+ autoload :CGIHandler, 'action_controller/cgi_process'
end
autoload :Mime, 'action_controller/mime_type'
diff --git a/actionpack/lib/action_controller/assertions/response_assertions.rb b/actionpack/lib/action_controller/assertions/response_assertions.rb
index 7ab24389b8..5976090273 100644
--- a/actionpack/lib/action_controller/assertions/response_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/response_assertions.rb
@@ -16,7 +16,7 @@ module ActionController
# ==== Examples
#
# # assert that the response was a redirection
- # assert_response :redirect
+ # assert_response :redirect
#
# # assert that the response code was status code 401 (unauthorized)
# assert_response 401
@@ -41,7 +41,7 @@ module ActionController
end
end
- # Assert that the redirection options passed in match those of the redirect called in the latest action.
+ # Assert that the redirection options passed in match those of the redirect called in the latest action.
# This match can be partial, such that assert_redirected_to(:controller => "weblog") will also
# match the redirection of redirect_to(:controller => "weblog", :action => "show") and so on.
#
@@ -60,12 +60,12 @@ module ActionController
clean_backtrace do
assert_response(:redirect, message)
return true if options == @response.redirected_to
-
+
# Support partial arguments for hash redirections
if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash)
return true if options.all? {|(key, value)| @response.redirected_to[key] == value}
end
-
+
redirected_to_after_normalisation = normalize_argument_to_redirection(@response.redirected_to)
options_after_normalisation = normalize_argument_to_redirection(options)
@@ -75,29 +75,59 @@ module ActionController
end
end
- # Asserts that the request was rendered with the appropriate template file.
+ # Asserts that the request was rendered with the appropriate template file or partials
#
# ==== Examples
#
# # assert that the "new" view template was rendered
# assert_template "new"
#
- def assert_template(expected = nil, message=nil)
+ # # assert that the "_customer" partial was rendered twice
+ # assert_template :partial => '_customer', :count => 2
+ #
+ # # assert that no partials were rendered
+ # assert_template :partial => false
+ #
+ def assert_template(options = {}, message = nil)
clean_backtrace do
- rendered = @response.rendered_template.to_s
- msg = build_message(message, "expecting <?> but rendering with <?>", expected, rendered)
- assert_block(msg) do
- if expected.nil?
- @response.rendered_template.blank?
+ case options
+ when NilClass, String
+ rendered = @response.rendered[:template].to_s
+ msg = build_message(message,
+ "expecting <?> but rendering with <?>",
+ options, rendered)
+ assert_block(msg) do
+ if options.nil?
+ @response.rendered[:template].blank?
+ else
+ rendered.to_s.match(options)
+ end
+ end
+ when Hash
+ if expected_partial = options[:partial]
+ partials = @response.rendered[:partials]
+ if expected_count = options[:count]
+ found = partials.detect { |p, _| p.to_s.match(expected_partial) }
+ actual_count = found.nil? ? 0 : found.second
+ msg = build_message(message,
+ "expecting ? to be rendered ? time(s) but rendered ? time(s)",
+ expected_partial, expected_count, actual_count)
+ assert(actual_count == expected_count.to_i, msg)
+ else
+ msg = build_message(message,
+ "expecting partial <?> but action rendered <?>",
+ options[:partial], partials.keys)
+ assert(partials.keys.any? { |p| p.to_s.match(expected_partial) }, msg)
+ end
else
- rendered.to_s.match(expected)
+ assert @response.rendered[:partials].empty?,
+ "Expected no partials to be rendered"
end
end
end
end
private
-
# Proxy to to_param if the object will respond to it.
def parameterize(value)
value.respond_to?(:to_param) ? value.to_param : value
diff --git a/actionpack/lib/action_controller/assertions/routing_assertions.rb b/actionpack/lib/action_controller/assertions/routing_assertions.rb
index 8a837c592c..5101751cea 100644
--- a/actionpack/lib/action_controller/assertions/routing_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/routing_assertions.rb
@@ -134,7 +134,7 @@ module ActionController
path = "/#{path}" unless path.first == '/'
# Assume given controller
- request = ActionController::TestRequest.new({}, {}, nil)
+ request = ActionController::TestRequest.new
request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
request.path = path
diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/assertions/selector_assertions.rb
index e03fed7abb..7f8fe9ab19 100644
--- a/actionpack/lib/action_controller/assertions/selector_assertions.rb
+++ b/actionpack/lib/action_controller/assertions/selector_assertions.rb
@@ -402,6 +402,7 @@ module ActionController
if rjs_type
if rjs_type == :insert
position = args.shift
+ id = args.shift
insertion = "insert_#{position}".to_sym
raise ArgumentError, "Unknown RJS insertion type #{position}" unless RJS_STATEMENTS[insertion]
statement = "(#{RJS_STATEMENTS[insertion]})"
@@ -587,7 +588,7 @@ module ActionController
def response_from_page_or_rjs()
content_type = @response.content_type
- if content_type && content_type =~ /text\/javascript/
+ if content_type && Mime::JS =~ content_type
body = @response.body.dup
root = HTML::Node.new(nil)
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index c2f0c1c4f6..e22114195c 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -164,8 +164,8 @@ module ActionController #:nodoc:
#
# Other options for session storage are:
#
- # * ActiveRecordStore - 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 ActiveRecordStore, set
+ # * 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
#
# config.action_controller.session_store = :active_record_store
#
@@ -254,7 +254,7 @@ module ActionController #:nodoc:
cattr_reader :protected_instance_variables
# Controller specific instance variables which will not be accessible inside views.
@@protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller
- @action_name @before_filter_chain_aborted @action_cache_path @_session @_cookies @_headers @_params
+ @action_name @before_filter_chain_aborted @action_cache_path @_session @_headers @_params
@_flash @_response)
# Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets,
@@ -382,6 +382,13 @@ module ActionController #:nodoc:
attr_accessor :action_name
class << self
+ def call(env)
+ # HACK: For global rescue to have access to the original request and response
+ request = env["action_controller.rescue.request"] ||= Request.new(env)
+ response = env["action_controller.rescue.response"] ||= Response.new
+ process(request, response)
+ end
+
# Factory for the standard create, process loop where the controller is discarded after processing.
def process(request, response) #:nodoc:
new.process(request, response)
@@ -502,7 +509,7 @@ module ActionController #:nodoc:
protected :filter_parameters
end
- delegate :exempt_from_layout, :to => 'ActionView::Base'
+ delegate :exempt_from_layout, :to => 'ActionView::Template'
end
public
@@ -859,16 +866,23 @@ module ActionController #:nodoc:
def render(options = nil, extra_options = {}, &block) #:doc:
raise DoubleRenderError, "Can only render or redirect once per action" if performed?
+ validate_render_arguments(options, extra_options, block_given?)
+
if options.nil?
- return render(:file => default_template_name, :layout => true)
- elsif !extra_options.is_a?(Hash)
- raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}"
- else
- if options == :update
- options = extra_options.merge({ :update => true })
- elsif !options.is_a?(Hash)
- raise RenderError, "You called render with invalid options : #{options.inspect}"
+ options = { :template => default_template, :layout => true }
+ elsif options == :update
+ options = extra_options.merge({ :update => true })
+ elsif options.is_a?(String) || options.is_a?(Symbol)
+ case options.to_s.index('/')
+ when 0
+ extra_options[:file] = options
+ when nil
+ extra_options[:action] = options
+ else
+ extra_options[:template] = options
end
+
+ options = extra_options
end
layout = pick_layout(options)
@@ -898,7 +912,7 @@ module ActionController #:nodoc:
render_for_text(@template.render(options.merge(:layout => layout)), options[:status])
elsif action_name = options[:action]
- render_for_file(default_template_name(action_name.to_s), options[:status], layout)
+ render_for_file(default_template(action_name.to_s), options[:status], layout)
elsif xml = options[:xml]
response.content_type ||= Mime::XML
@@ -933,7 +947,7 @@ module ActionController #:nodoc:
render_for_text(nil, options[:status])
else
- render_for_file(default_template_name, options[:status], layout)
+ render_for_file(default_template, options[:status], layout)
end
end
end
@@ -990,7 +1004,7 @@ module ActionController #:nodoc:
@performed_redirect = false
response.redirected_to = nil
response.redirected_to_method_params = nil
- response.headers['Status'] = DEFAULT_RENDER_STATUS_CODE
+ response.status = DEFAULT_RENDER_STATUS_CODE
response.headers.delete('Location')
end
@@ -1111,7 +1125,7 @@ module ActionController #:nodoc:
end
# Sets the etag, last_modified, or both on the response and renders a
- # "304 Not Modified" response if the request is already fresh.
+ # "304 Not Modified" response if the request is already fresh.
#
# Example:
#
@@ -1119,8 +1133,8 @@ module ActionController #:nodoc:
# @article = Article.find(params[:id])
# fresh_when(:etag => @article, :last_modified => @article.created_at.utc)
# end
- #
- # This will render the show template if the request isn't sending a matching etag or
+ #
+ # This will render the show template if the request isn't sending a matching etag or
# If-Modified-Since header and just a "304 Not Modified" response if there's a match.
def fresh_when(options)
options.assert_valid_keys(:etag, :last_modified)
@@ -1160,20 +1174,19 @@ module ActionController #:nodoc:
def reset_session #:doc:
request.reset_session
@_session = request.session
- response.session = @_session
end
-
private
def render_for_file(template_path, status = nil, layout = nil, locals = {}) #:nodoc:
- logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
+ path = template_path.respond_to?(:path_without_format_and_extension) ? template_path.path_without_format_and_extension : template_path
+ logger.info("Rendering #{path}" + (status ? " (#{status})" : '')) if logger
render_for_text @template.render(:file => template_path, :locals => locals, :layout => layout), status
end
def render_for_text(text = nil, status = nil, append_response = false) #:nodoc:
@performed_render = true
- response.headers['Status'] = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
+ response.status = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
if append_response
response.body ||= ''
@@ -1187,6 +1200,16 @@ module ActionController #:nodoc:
end
end
+ def validate_render_arguments(options, extra_options, has_block)
+ if options && (has_block && options != :update) && !options.is_a?(String) && !options.is_a?(Hash) && !options.is_a?(Symbol)
+ raise RenderError, "You called render with invalid options : #{options.inspect}"
+ end
+
+ if !extra_options.is_a?(Hash)
+ raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}"
+ end
+ end
+
def initialize_template_class(response)
response.template = ActionView::Base.new(self.class.view_paths, {}, self)
response.template.helpers.send :include, self.class.master_helper_module
@@ -1195,7 +1218,7 @@ module ActionController #:nodoc:
end
def assign_shortcuts(request, response)
- @_request, @_params, @_cookies = request, request.parameters, request.cookies
+ @_request, @_params = request, request.parameters
@_response = response
@_response.session = request.session
@@ -1213,11 +1236,10 @@ module ActionController #:nodoc:
def log_processing
if logger && logger.info?
log_processing_for_request_id
- log_processing_for_session_id
log_processing_for_parameters
end
end
-
+
def log_processing_for_request_id
request_id = "\n\nProcessing #{self.class.name}\##{action_name} "
request_id << "to #{params[:format]} " if params[:format]
@@ -1226,17 +1248,10 @@ module ActionController #:nodoc:
logger.info(request_id)
end
- def log_processing_for_session_id
- if @_session && @_session.respond_to?(:session_id) && @_session.respond_to?(:dbman) &&
- !@_session.dbman.is_a?(CGI::Session::CookieStore)
- logger.info " Session ID: #{@_session.session_id}"
- end
- end
-
def log_processing_for_parameters
parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
parameters = parameters.except!(:controller, :action, :format, :_method)
-
+
logger.info " Parameters: #{parameters.inspect}" unless parameters.empty?
end
@@ -1251,10 +1266,17 @@ module ActionController #:nodoc:
elsif respond_to? :method_missing
method_missing action_name
default_render unless performed?
- elsif template_exists?
- default_render
else
- raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller
+ begin
+ default_render
+ rescue ActionView::MissingTemplate => e
+ # Was the implicit template missing, or was it another template?
+ if e.path == default_template_name
+ raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller
+ else
+ raise e
+ end
+ end
end
end
@@ -1300,10 +1322,8 @@ module ActionController #:nodoc:
@_session.close if @_session && @_session.respond_to?(:close)
end
- def template_exists?(template_name = default_template_name)
- @template.send(:_pick_template, template_name) ? true : false
- rescue ActionView::MissingTemplate
- false
+ def default_template(action_name = self.action_name)
+ self.view_paths.find_template(default_template_name(action_name), default_template_format)
end
def default_template_name(action_name = self.action_name)
@@ -1330,9 +1350,12 @@ module ActionController #:nodoc:
end
Base.class_eval do
- include Flash, Filters, Layout, Benchmarking, Rescue, MimeResponds, Helpers
- include Cookies, Caching, Verification, Streaming
- include SessionManagement, HttpAuthentication::Basic::ControllerMethods
- include RecordIdentifier, RequestForgeryProtection, Translation
+ [ Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers,
+ Cookies, Caching, Verification, Streaming, SessionManagement,
+ HttpAuthentication::Basic::ControllerMethods, RecordIdentifier,
+ RequestForgeryProtection, Translation
+ ].each do |mod|
+ include mod
+ end
end
end
diff --git a/actionpack/lib/action_controller/benchmarking.rb b/actionpack/lib/action_controller/benchmarking.rb
index fa572ebf3d..47377e5fa9 100644
--- a/actionpack/lib/action_controller/benchmarking.rb
+++ b/actionpack/lib/action_controller/benchmarking.rb
@@ -23,8 +23,8 @@ module ActionController #:nodoc:
def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
if logger && logger.level == log_level
result = nil
- seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield }
- logger.add(log_level, "#{title} (#{('%.1f' % (seconds * 1000))}ms)")
+ ms = Benchmark.ms { result = use_silence ? silence { yield } : yield }
+ logger.add(log_level, "#{title} (#{('%.1f' % ms)}ms)")
result
else
yield
@@ -48,7 +48,7 @@ module ActionController #:nodoc:
end
render_output = nil
- @view_runtime = Benchmark::realtime { render_output = render_without_benchmark(options, extra_options, &block) }
+ @view_runtime = Benchmark.ms { render_output = render_without_benchmark(options, extra_options, &block) }
if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
@db_rt_before_render = db_runtime
@@ -65,11 +65,11 @@ module ActionController #:nodoc:
private
def perform_action_with_benchmark
if logger
- seconds = [ Benchmark::measure{ perform_action_without_benchmark }.real, 0.0001 ].max
+ ms = [Benchmark.ms { perform_action_without_benchmark }, 0.01].max
logging_view = defined?(@view_runtime)
logging_active_record = Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
- log_message = "Completed in #{sprintf("%.0f", seconds * 1000)}ms"
+ log_message = 'Completed in %.0fms' % ms
if logging_view || logging_active_record
log_message << " ("
@@ -83,25 +83,25 @@ module ActionController #:nodoc:
end
end
- log_message << " | #{headers["Status"]}"
+ log_message << " | #{response.status}"
log_message << " [#{complete_request_uri rescue "unknown"}]"
logger.info(log_message)
- response.headers["X-Runtime"] = "#{sprintf("%.0f", seconds * 1000)}ms"
+ response.headers["X-Runtime"] = "%.0f" % ms
else
perform_action_without_benchmark
end
end
def view_runtime
- "View: %.0f" % (@view_runtime * 1000)
+ "View: %.0f" % @view_runtime
end
def active_record_runtime
db_runtime = ActiveRecord::Base.connection.reset_runtime
db_runtime += @db_rt_before_render if @db_rt_before_render
db_runtime += @db_rt_after_render if @db_rt_after_render
- "DB: %.0f" % (db_runtime * 1000)
+ "DB: %.0f" % db_runtime
end
end
end
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index b4d251eb3c..1d14df0052 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -27,7 +27,6 @@ module ActionController #:nodoc:
autoload :Actions, 'action_controller/caching/actions'
autoload :Fragments, 'action_controller/caching/fragments'
autoload :Pages, 'action_controller/caching/pages'
- autoload :SqlCache, 'action_controller/caching/sql_cache'
autoload :Sweeping, 'action_controller/caching/sweeping'
def self.included(base) #:nodoc:
@@ -41,7 +40,7 @@ module ActionController #:nodoc:
end
include Pages, Actions, Fragments
- include Sweeping, SqlCache if defined?(ActiveRecord)
+ include Sweeping if defined?(ActiveRecord)
@@perform_caching = true
cattr_accessor :perform_caching
diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb
index 7c803a9830..34e1c3527f 100644
--- a/actionpack/lib/action_controller/caching/actions.rb
+++ b/actionpack/lib/action_controller/caching/actions.rb
@@ -113,7 +113,7 @@ module ActionController #:nodoc:
end
def caching_allowed(controller)
- controller.request.get? && controller.response.headers['Status'].to_i == 200
+ controller.request.get? && controller.response.status.to_i == 200
end
def cache_layout?
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index 22e4fbec43..bd3b5a5875 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -145,7 +145,7 @@ module ActionController #:nodoc:
private
def caching_allowed
- request.get? && response.headers['Status'].to_i == 200
+ request.get? && response.status.to_i == 200
end
end
end
diff --git a/actionpack/lib/action_controller/caching/sql_cache.rb b/actionpack/lib/action_controller/caching/sql_cache.rb
deleted file mode 100644
index 139be6100d..0000000000
--- a/actionpack/lib/action_controller/caching/sql_cache.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module ActionController #:nodoc:
- module Caching
- module SqlCache
- def self.included(base) #:nodoc:
- if defined?(ActiveRecord) && ActiveRecord::Base.respond_to?(:cache)
- base.alias_method_chain :perform_action, :caching
- end
- end
-
- protected
- def perform_action_with_caching
- ActiveRecord::Base.cache do
- perform_action_without_caching
- end
- end
- end
- end
-end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/cgi_ext.rb b/actionpack/lib/action_controller/cgi_ext.rb
index f3b8c08d8f..406b6f06d6 100644
--- a/actionpack/lib/action_controller/cgi_ext.rb
+++ b/actionpack/lib/action_controller/cgi_ext.rb
@@ -1,7 +1,6 @@
require 'action_controller/cgi_ext/stdinput'
require 'action_controller/cgi_ext/query_extension'
require 'action_controller/cgi_ext/cookie'
-require 'action_controller/cgi_ext/session'
class CGI #:nodoc:
include ActionController::CgiExt::Stdinput
diff --git a/actionpack/lib/action_controller/cgi_ext/session.rb b/actionpack/lib/action_controller/cgi_ext/session.rb
deleted file mode 100644
index d3f85e3705..0000000000
--- a/actionpack/lib/action_controller/cgi_ext/session.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-require 'digest/md5'
-require 'cgi/session'
-require 'cgi/session/pstore'
-
-class CGI #:nodoc:
- # * Expose the CGI instance to session stores.
- # * Don't require 'digest/md5' whenever a new session id is generated.
- class Session #:nodoc:
- def self.generate_unique_id(constant = nil)
- ActiveSupport::SecureRandom.hex(16)
- end
-
- # Make the CGI instance available to session stores.
- attr_reader :cgi
- attr_reader :dbman
- alias_method :initialize_without_cgi_reader, :initialize
- def initialize(cgi, options = {})
- @cgi = cgi
- initialize_without_cgi_reader(cgi, options)
- end
-
- private
- # Create a new session id.
- def create_new_id
- @new_session = true
- self.class.generate_unique_id
- end
-
- # * Don't require 'digest/md5' whenever a new session is started.
- class PStore #:nodoc:
- def initialize(session, option={})
- dir = option['tmpdir'] || Dir::tmpdir
- prefix = option['prefix'] || ''
- id = session.session_id
- md5 = Digest::MD5.hexdigest(id)[0,16]
- path = dir+"/"+prefix+md5
- path.untaint
- if File::exist?(path)
- @hash = nil
- else
- unless session.new_session
- raise CGI::Session::NoSession, "uninitialized session"
- end
- @hash = {}
- end
- @p = ::PStore.new(path)
- @p.transaction do |p|
- File.chmod(0600, p.path)
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/cgi_process.rb b/actionpack/lib/action_controller/cgi_process.rb
index 5d6988e1b1..7e5e95e135 100644
--- a/actionpack/lib/action_controller/cgi_process.rb
+++ b/actionpack/lib/action_controller/cgi_process.rb
@@ -61,7 +61,7 @@ module ActionController #:nodoc:
class CgiRequest #:nodoc:
DEFAULT_SESSION_OPTIONS = {
- :database_manager => CGI::Session::CookieStore,
+ :database_manager => nil,
:prefix => "ruby_sess.",
:session_path => "/",
:session_key => "_session_id",
diff --git a/actionpack/lib/action_controller/cookies.rb b/actionpack/lib/action_controller/cookies.rb
index 0428f2a23d..840ceb5abd 100644
--- a/actionpack/lib/action_controller/cookies.rb
+++ b/actionpack/lib/action_controller/cookies.rb
@@ -64,43 +64,31 @@ module ActionController #:nodoc:
# Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
def [](name)
- cookie = @cookies[name.to_s]
- if cookie && cookie.respond_to?(:value)
- cookie.size > 1 ? cookie.value : cookie.value[0]
- end
+ super(name.to_s)
end
# Sets the cookie named +name+. The second argument may be the very cookie
# value, or a hash of options as documented above.
- def []=(name, options)
+ def []=(key, options)
if options.is_a?(Hash)
- options = options.inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
- options["name"] = name.to_s
+ options.symbolize_keys!
else
- options = { "name" => name.to_s, "value" => options }
+ options = { :value => options }
end
- set_cookie(options)
+ options[:path] = "/" unless options.has_key?(:path)
+ super(key.to_s, options[:value])
+ @controller.response.set_cookie(key, options)
end
# Removes the cookie on the client machine by setting the value to an empty string
# and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
# an options hash to delete cookies with extra data such as a <tt>:path</tt>.
- def delete(name, options = {})
- options.stringify_keys!
- set_cookie(options.merge("name" => name.to_s, "value" => "", "expires" => Time.at(0)))
+ def delete(key, options = {})
+ options.symbolize_keys!
+ options[:path] = "/" unless options.has_key?(:path)
+ super(key.to_s)
+ @controller.response.delete_cookie(key, options)
end
-
- private
- # Builds a CGI::Cookie object and adds the cookie to the response headers.
- #
- # The path of the cookie defaults to "/" if there's none in +options+, and
- # everything is passed to the CGI::Cookie constructor.
- def set_cookie(options) #:doc:
- options["path"] = "/" unless options["path"]
- cookie = CGI::Cookie.new(options)
- @controller.logger.info "Cookie set: #{cookie}" unless @controller.logger.nil?
- @controller.response.headers["cookie"] << cookie
- end
end
end
diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb
index 203f6b1683..781bc48887 100644
--- a/actionpack/lib/action_controller/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatcher.rb
@@ -2,14 +2,14 @@ module ActionController
# Dispatches requests to the appropriate controller and takes care of
# reloading the app after each request when Dependencies.load? is true.
class Dispatcher
- @@guard = Mutex.new
-
class << self
def define_dispatcher_callbacks(cache_classes)
unless cache_classes
# Development mode callbacks
before_dispatch :reload_application
after_dispatch :cleanup_application
+
+ ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
end
if defined?(ActiveRecord)
@@ -45,39 +45,35 @@ module ActionController
end
cattr_accessor :middleware
- self.middleware = MiddlewareStack.new
- self.middleware.use "ActionController::Failsafe"
+ self.middleware = MiddlewareStack.new do |middleware|
+ middlewares = File.join(File.dirname(__FILE__), "middlewares.rb")
+ middleware.instance_eval(File.read(middlewares))
+ end
include ActiveSupport::Callbacks
define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
- # DEPRECATE: Remove arguments
+ # DEPRECATE: Remove arguments, since they are only used by CGI
def initialize(output = $stdout, request = nil, response = nil)
- @output, @request, @response = output, request, response
+ @output = output
@app = @@middleware.build(lambda { |env| self.dup._call(env) })
end
- def dispatch_unlocked
+ def dispatch
begin
run_callbacks :before_dispatch
- handle_request
+ Routing::Routes.call(@env)
rescue Exception => exception
- failsafe_rescue exception
+ if controller ||= (::ApplicationController rescue Base)
+ controller.call_with_exception(@env, exception).to_a
+ else
+ raise exception
+ end
ensure
run_callbacks :after_dispatch, :enumerator => :reverse_each
end
end
- def dispatch
- if ActionController::Base.allow_concurrency
- dispatch_unlocked
- else
- @@guard.synchronize do
- dispatch_unlocked
- end
- end
- end
-
# DEPRECATE: Remove CGI support
def dispatch_cgi(cgi, session_options)
CGIHandler.dispatch_cgi(self, cgi, @output)
@@ -88,8 +84,7 @@ module ActionController
end
def _call(env)
- @request = RackRequest.new(env)
- @response = RackResponse.new(@request)
+ @env = env
dispatch
end
@@ -98,7 +93,6 @@ module ActionController
run_callbacks :prepare_dispatch
Routing::Routes.reload
- ActionView::Helpers::AssetTagHelper::AssetTag::Cache.clear
end
# Cleanup the application by clearing out loaded classes so they can
@@ -115,23 +109,8 @@ module ActionController
def checkin_connections
# Don't return connection (and peform implicit rollback) if this request is a part of integration test
- # TODO: This callback should have direct access to env
- return if @request.key?("action_controller.test")
+ return if @env.key?("rack.test")
ActiveRecord::Base.clear_active_connections!
end
-
- protected
- def handle_request
- @controller = Routing::Routes.recognize(@request)
- @controller.process(@request, @response).out
- end
-
- def failsafe_rescue(exception)
- if @controller ||= (::ApplicationController rescue Base)
- @controller.process_with_exception(@request, @response, exception).out
- else
- raise exception
- end
- end
end
end
diff --git a/actionpack/lib/action_controller/failsafe.rb b/actionpack/lib/action_controller/failsafe.rb
index bb6ef39470..567581142c 100644
--- a/actionpack/lib/action_controller/failsafe.rb
+++ b/actionpack/lib/action_controller/failsafe.rb
@@ -11,7 +11,7 @@ module ActionController
@app.call(env)
rescue Exception => exception
# Reraise exception in test environment
- if env["action_controller.test"]
+ if env["rack.test"]
raise exception
else
failsafe_response(exception)
@@ -42,8 +42,8 @@ module ActionController
end
def failsafe_logger
- if defined?(::RAILS_DEFAULT_LOGGER) && !::RAILS_DEFAULT_LOGGER.nil?
- ::RAILS_DEFAULT_LOGGER
+ if defined?(Rails) && Rails.logger
+ Rails.logger
else
Logger.new($stderr)
end
diff --git a/actionpack/lib/action_controller/flash.rb b/actionpack/lib/action_controller/flash.rb
index 62fa381a6f..9856dbed2a 100644
--- a/actionpack/lib/action_controller/flash.rb
+++ b/actionpack/lib/action_controller/flash.rb
@@ -27,55 +27,54 @@ module ActionController #:nodoc:
def self.included(base)
base.class_eval do
include InstanceMethods
- alias_method_chain :assign_shortcuts, :flash
- alias_method_chain :reset_session, :flash
+ alias_method_chain :perform_action, :flash
+ alias_method_chain :reset_session, :flash
end
end
-
-
+
class FlashNow #:nodoc:
def initialize(flash)
@flash = flash
end
-
+
def []=(k, v)
@flash[k] = v
@flash.discard(k)
v
end
-
+
def [](k)
@flash[k]
end
end
-
+
class FlashHash < Hash
def initialize #:nodoc:
super
@used = {}
end
-
+
def []=(k, v) #:nodoc:
keep(k)
super
end
-
+
def update(h) #:nodoc:
h.keys.each { |k| keep(k) }
super
end
-
+
alias :merge! :update
-
+
def replace(h) #:nodoc:
@used = {}
super
end
-
+
# Sets a flash that will not be available to the next action, only to the current.
#
# flash.now[:message] = "Hello current action"
- #
+ #
# This method enables you to use the flash as a central messaging system in your app.
# When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
# When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
@@ -85,7 +84,7 @@ module ActionController #:nodoc:
def now
FlashNow.new(self)
end
-
+
# Keeps either the entire current flash or a specific flash entry available for the next action:
#
# flash.keep # keeps the entire flash
@@ -93,7 +92,7 @@ module ActionController #:nodoc:
def keep(k = nil)
use(k, false)
end
-
+
# Marks the entire flash or a single flash entry to be discarded by the end of the current action:
#
# flash.discard # discard the entire flash at the end of the current action
@@ -101,12 +100,12 @@ module ActionController #:nodoc:
def discard(k = nil)
use(k)
end
-
+
# Mark for removal entries that were kept, and delete unkept ones.
#
# This method is called automatically by filters, so you generally don't need to care about it.
def sweep #:nodoc:
- keys.each do |k|
+ keys.each do |k|
unless @used[k]
use(k)
else
@@ -118,7 +117,7 @@ module ActionController #:nodoc:
# clean up after keys that could have been left over by calling reject! or shift on the flash
(@used.keys - keys).each{ |k| @used.delete(k) }
end
-
+
private
# Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
# use() # marks the entire flash as used
@@ -136,37 +135,27 @@ module ActionController #:nodoc:
module InstanceMethods #:nodoc:
protected
+ def perform_action_with_flash
+ perform_action_without_flash
+ remove_instance_variable(:@_flash) if defined? @_flash
+ end
+
def reset_session_with_flash
reset_session_without_flash
- remove_instance_variable(:@_flash)
- flash(:refresh)
+ remove_instance_variable(:@_flash) if defined? @_flash
end
-
- # Access the contents of the flash. Use <tt>flash["notice"]</tt> to read a notice you put there or
- # <tt>flash["notice"] = "hello"</tt> to put a new one.
- # Note that if sessions are disabled only flash.now will work.
- def flash(refresh = false) #:doc:
- if !defined?(@_flash) || refresh
- @_flash =
- if session.is_a?(Hash)
- # don't put flash in session if disabled
- FlashHash.new
- else
- # otherwise, session is a CGI::Session or a TestSession
- # so make sure it gets retrieved from/saved to session storage after request processing
- session["flash"] ||= FlashHash.new
- end
+
+ # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
+ # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
+ # to put a new one.
+ def flash #:doc:
+ unless defined? @_flash
+ @_flash = session["flash"] ||= FlashHash.new
+ @_flash.sweep
end
@_flash
end
-
- private
- def assign_shortcuts_with_flash(request, response) #:nodoc:
- assign_shortcuts_without_flash(request, response)
- flash(:refresh)
- flash.sweep if @_session
- end
end
end
end
diff --git a/actionpack/lib/action_controller/helpers.rb b/actionpack/lib/action_controller/helpers.rb
index 402750c57d..ba65032f6a 100644
--- a/actionpack/lib/action_controller/helpers.rb
+++ b/actionpack/lib/action_controller/helpers.rb
@@ -163,9 +163,9 @@ module ActionController #:nodoc:
def helper_method(*methods)
methods.flatten.each do |method|
master_helper_module.module_eval <<-end_eval
- def #{method}(*args, &block)
- controller.send(%(#{method}), *args, &block)
- end
+ def #{method}(*args, &block) # def current_user(*args, &block)
+ controller.send(%(#{method}), *args, &block) # controller.send(%(current_user), *args, &block)
+ end # end
end_eval
end
end
diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb
index 2ed810db7d..3cb5829eca 100644
--- a/actionpack/lib/action_controller/http_authentication.rb
+++ b/actionpack/lib/action_controller/http_authentication.rb
@@ -55,7 +55,31 @@ module ActionController
# end
# end
#
- #
+ # Simple Digest example. Note the block must return the user's password so the framework
+ # can appropriately hash it to check the user's credentials. Returning nil will cause authentication to fail.
+ #
+ # class PostsController < ApplicationController
+ # Users = {"dhh" => "secret"}
+ #
+ # before_filter :authenticate, :except => [ :index ]
+ #
+ # def index
+ # render :text => "Everyone can see me!"
+ # end
+ #
+ # def edit
+ # render :text => "I'm only accessible if you know the password"
+ # end
+ #
+ # private
+ # def authenticate
+ # authenticate_or_request_with_http_digest(realm) do |user_name|
+ # Users[user_name]
+ # end
+ # end
+ # end
+ #
+ #
# In your integration tests, you can do something like this:
#
# def test_access_granted_from_xml
@@ -108,7 +132,10 @@ module ActionController
end
def decode_credentials(request)
- ActiveSupport::Base64.decode64(authorization(request).split.last || '')
+ # Properly decode credentials spanning a new-line
+ auth = authorization(request)
+ auth.slice!('Basic ')
+ ActiveSupport::Base64.decode64(auth || '')
end
def encode_credentials(user_name, password)
@@ -120,5 +147,165 @@ module ActionController
controller.__send__ :render, :text => "HTTP Basic: Access denied.\n", :status => :unauthorized
end
end
+
+ module Digest
+ extend self
+
+ module ControllerMethods
+ def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure)
+ begin
+ authenticate_with_http_digest!(realm, &password_procedure)
+ rescue ActionController::HttpAuthentication::Error => e
+ msg = e.message
+ msg = "#{msg} expected '#{e.expected}' was '#{e.was}'" unless e.expected.nil?
+ raise msg if e.fatal?
+ request_http_digest_authentication(realm, msg)
+ end
+ end
+
+ # Authenticate using HTTP Digest, throwing ActionController::HttpAuthentication::Error on failure.
+ # This allows more detailed analysis of authentication failures
+ # to be relayed to the client.
+ def authenticate_with_http_digest!(realm = "Application", &login_procedure)
+ HttpAuthentication::Digest.authenticate(self, realm, &login_procedure)
+ end
+
+ # Authenticate with HTTP Digest, returns true or false
+ def authenticate_with_http_digest(realm = "Application", &login_procedure)
+ HttpAuthentication::Digest.authenticate(self, realm, &login_procedure) rescue false
+ end
+
+ # Render output including the HTTP Digest authentication header
+ def request_http_digest_authentication(realm = "Application", message = nil)
+ HttpAuthentication::Digest.authentication_request(self, realm, message)
+ end
+
+ # Add HTTP Digest authentication header to result headers
+ def http_digest_authentication_header(realm = "Application")
+ HttpAuthentication::Digest.authentication_header(self, realm)
+ end
+ end
+
+ # Raises error unless authentictaion succeeds, returns true otherwise
+ def authenticate(controller, realm, &password_procedure)
+ raise Error.new(false), "No authorization header found" unless authorization(controller.request)
+ validate_digest_response(controller, realm, &password_procedure)
+ true
+ end
+
+ def authorization(request)
+ request.env['HTTP_AUTHORIZATION'] ||
+ request.env['X-HTTP_AUTHORIZATION'] ||
+ request.env['X_HTTP_AUTHORIZATION'] ||
+ request.env['REDIRECT_X_HTTP_AUTHORIZATION']
+ end
+
+ # Raises error unless the request credentials response value matches the expected value.
+ def validate_digest_response(controller, realm, &password_procedure)
+ credentials = decode_credentials(controller.request)
+
+ # Check the nonce, opaque and realm.
+ # Ignore nc, as we have no way to validate the number of times this nonce has been used
+ validate_nonce(controller.request, credentials[:nonce])
+ raise Error.new(false, realm, credentials[:realm]), "Realm doesn't match" unless realm == credentials[:realm]
+ raise Error.new(true, opaque(controller.request), credentials[:opaque]),"Opaque doesn't match" unless opaque(controller.request) == credentials[:opaque]
+
+ password = password_procedure.call(credentials[:username])
+ raise Error.new(false), "No password" if password.nil?
+ expected = expected_response(controller.request.env['REQUEST_METHOD'], controller.request.url, credentials, password)
+ raise Error.new(false, expected, credentials[:response]), "Invalid response" unless expected == credentials[:response]
+ end
+
+ # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
+ def expected_response(http_method, uri, credentials, password)
+ ha1 = ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
+ ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase,uri].join(':'))
+ ::Digest::MD5.hexdigest([ha1,credentials[:nonce], credentials[:nc], credentials[:cnonce],credentials[:qop],ha2].join(':'))
+ end
+
+ def encode_credentials(http_method, credentials, password)
+ credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password)
+ "Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ')
+ end
+
+ def decode_credentials(request)
+ authorization(request).to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair|
+ key, value = pair.split('=', 2)
+ hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '')
+ hash
+ end
+ end
+
+ def authentication_header(controller, realm)
+ controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce(controller.request)}", opaque="#{opaque(controller.request)}")
+ end
+
+ def authentication_request(controller, realm, message = "HTTP Digest: Access denied")
+ authentication_header(controller, realm)
+ controller.send! :render, :text => message, :status => :unauthorized
+ end
+
+ # Uses an MD5 digest based on time to generate a value to be used only once.
+ #
+ # A server-specified data string which should be uniquely generated each time a 401 response is made.
+ # It is recommended that this string be base64 or hexadecimal data.
+ # Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed.
+ #
+ # The contents of the nonce are implementation dependent.
+ # The quality of the implementation depends on a good choice.
+ # A nonce might, for example, be constructed as the base 64 encoding of
+ #
+ # => time-stamp H(time-stamp ":" ETag ":" private-key)
+ #
+ # where time-stamp is a server-generated time or other non-repeating value,
+ # ETag is the value of the HTTP ETag header associated with the requested entity,
+ # and private-key is data known only to the server.
+ # With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and
+ # reject the request if it did not match the nonce from that header or
+ # if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity.
+ # The inclusion of the ETag prevents a replay request for an updated version of the resource.
+ # (Note: including the IP address of the client in the nonce would appear to offer the server the ability
+ # to limit the reuse of the nonce to the same client that originally got it.
+ # However, that would break proxy farms, where requests from a single user often go through different proxies in the farm.
+ # Also, IP address spoofing is not that hard.)
+ #
+ # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to
+ # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for
+ # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
+ # of this document.
+ #
+ # The nonce is opaque to the client.
+ def nonce(request, time = Time.now)
+ session_id = request.is_a?(String) ? request : request.session.session_id
+ t = time.to_i
+ hashed = [t, session_id]
+ digest = ::Digest::MD5.hexdigest(hashed.join(":"))
+ Base64.encode64("#{t}:#{digest}").gsub("\n", '')
+ end
+
+ def validate_nonce(request, value)
+ t = Base64.decode64(value).split(":").first.to_i
+ raise Error.new(true), "Stale Nonce" if (t - Time.now.to_i).abs > 10 * 60
+ n = nonce(request, t)
+ raise Error.new(true, value, n), "Bad Nonce" unless n == value
+ end
+
+ # Opaque based on digest of session_id
+ def opaque(request)
+ session_id = request.is_a?(String) ? request : request.session.session_id
+ @opaque ||= Base64.encode64(::Digest::MD5::hexdigest(session_id)).gsub("\n", '')
+ end
+ end
+
+ class Error < RuntimeError
+ attr_accessor :expected, :was
+ def initialize(fatal = false, expected = nil, was = nil)
+ @fatal = fatal
+ @expected = expected
+ @was = was
+ end
+
+ def fatal?; @fatal; end
+ end
end
end
diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb
index eeabe5b845..ded72a71fb 100644
--- a/actionpack/lib/action_controller/integration.rb
+++ b/actionpack/lib/action_controller/integration.rb
@@ -2,6 +2,17 @@ require 'stringio'
require 'uri'
require 'active_support/test_case'
+# Monkey patch Rack::Lint to support rewind
+module Rack
+ class Lint
+ class InputWrapper
+ def rewind
+ @input.rewind
+ end
+ end
+ end
+end
+
module ActionController
module Integration #:nodoc:
# An integration Session instance represents a set of requests and responses
@@ -57,12 +68,21 @@ module ActionController
# A running counter of the number of requests processed.
attr_accessor :request_count
+ # Nonce value for Digest Authentication, implicitly set on response with WWW-Authentication
+ attr_accessor :nonce
+
+ # Opaque value for Digest Authentication, implicitly set on response with WWW-Authentication
+ attr_accessor :opaque
+
+ # Opaque value for Authentication, implicitly set on response with WWW-Authentication
+ attr_accessor :realm
+
class MultiPartNeededException < Exception
end
# Create and initialize a new Session instance.
- def initialize(app)
- @application = app
+ def initialize(app = nil)
+ @application = app || ActionController::Dispatcher.new
reset!
end
@@ -126,7 +146,7 @@ module ActionController
# performed on the location header.
def follow_redirect!
raise "not a redirect! #{@status} #{@status_message}" unless redirect?
- get(interpret_uri(headers['location'].first))
+ get(interpret_uri(headers['location']))
status
end
@@ -181,7 +201,7 @@ module ActionController
# - +headers+: Additional HTTP headers to pass, as a Hash. The keys will
# automatically be upcased, with the prefix 'HTTP_' added if needed.
#
- # This method returns an AbstractResponse object, which one can use to
+ # This method returns an Response object, which one can use to
# inspect the details of the response. Furthermore, if this method was
# called from an ActionController::IntegrationTest object, then that
# object's <tt>@response</tt> instance variable will point to the same
@@ -227,13 +247,58 @@ module ActionController
def xml_http_request(request_method, path, parameters = nil, headers = nil)
headers ||= {}
headers['X-Requested-With'] = 'XMLHttpRequest'
- headers['Accept'] ||= 'text/javascript, text/html, application/xml, ' +
- 'text/xml, */*'
-
+ headers['Accept'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
process(request_method, path, parameters, headers)
end
alias xhr :xml_http_request
+ def request_with_noauth(http_method, uri, parameters, headers)
+ process_with_auth http_method, uri, parameters, headers
+ end
+
+ # Performs a request with the given http_method and parameters, including HTTP Basic authorization headers.
+ # See get() for more details on paramters and headers.
+ #
+ # You can perform GET, POST, PUT, DELETE, and HEAD requests with #get_with_basic, #post_with_basic,
+ # #put_with_basic, #delete_with_basic, and #head_with_basic.
+ def request_with_basic(http_method, uri, parameters, headers, user_name, password)
+ process_with_auth http_method, uri, parameters, headers.merge(:authorization => ActionController::HttpAuthentication::Basic.encode_credentials(user_name, password))
+ end
+
+ # Performs a request with the given http_method and parameters, including HTTP Digest authorization headers.
+ # See get() for more details on paramters and headers.
+ #
+ # You can perform GET, POST, PUT, DELETE, and HEAD requests with #get_with_digest, #post_with_digest,
+ # #put_with_digest, #delete_with_digest, and #head_with_digest.
+ def request_with_digest(http_method, uri, parameters, headers, user_name, password)
+ # Realm, Nonce, and Opaque taken from previoius 401 response
+
+ credentials = {
+ :username => user_name,
+ :realm => @realm,
+ :nonce => @nonce,
+ :qop => "auth",
+ :nc => "00000001",
+ :cnonce => "0a4f113b",
+ :opaque => @opaque,
+ :uri => uri
+ }
+
+ raise "Digest request without previous 401 response" if @opaque.nil?
+
+ process_with_auth http_method, uri, parameters, headers.merge(:authorization => ActionController::HttpAuthentication::Digest.encode_credentials(http_method, credentials, password))
+ end
+
+ # def get_with_basic, def post_with_basic, def put_with_basic, def delete_with_basic, def head_with_basic
+ # def get_with_digest, def post_with_digest, def put_with_digest, def delete_with_digest, def head_with_digest
+ [:get, :post, :put, :delete, :head].each do |method|
+ [:noauth, :basic, :digest].each do |auth_type|
+ define_method("#{method}_with_#{auth_type}") do |uri, parameters, headers, *auth|
+ send("request_with_#{auth_type}", method, uri, parameters, headers, *auth)
+ end
+ end
+ end
+
# Returns the URL for the given options, according to the rules specified
# in the application's routes.
def url_for(options)
@@ -278,6 +343,7 @@ module ActionController
"SCRIPT_NAME" => "",
"REQUEST_URI" => path,
+ "PATH_INFO" => path,
"HTTP_HOST" => host,
"REMOTE_ADDR" => remote_addr,
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
@@ -292,7 +358,7 @@ module ActionController
"rack.multiprocess" => true,
"rack.run_once" => false,
- "action_controller.test" => true
+ "rack.test" => true
)
(headers || {}).each do |key, value|
@@ -301,8 +367,10 @@ module ActionController
env[key] = value
end
- unless ActionController::Base.respond_to?(:clear_last_instantiation!)
- ActionController::Base.module_eval { include ControllerCapture }
+ [ControllerCapture, ActionController::ProcessWithTest].each do |mod|
+ unless ActionController::Base < mod
+ ActionController::Base.class_eval { include mod }
+ end
end
ActionController::Base.clear_last_instantiation!
@@ -312,16 +380,6 @@ module ActionController
status, headers, body = app.call(env)
@request_count += 1
- if @controller = ActionController::Base.last_instantiation
- @request = @controller.request
- @response = @controller.response
-
- # Decorate the response with the standard behavior of the
- # TestResponse so that things like assert_response can be
- # used in integration tests.
- @response.extend(TestResponseBehavior)
- end
-
@html_document = nil
@status = status.to_i
@@ -337,6 +395,24 @@ module ActionController
@body = ""
body.each { |part| @body << part }
+ if @controller = ActionController::Base.last_instantiation
+ @request = @controller.request
+ @response = @controller.response
+ @controller.send(:set_test_assigns)
+ else
+ # Decorate responses from Rack Middleware and Rails Metal
+ # as an Response for the purposes of integration testing
+ @response = Response.new
+ @response.status = status.to_s
+ @response.headers.replace(@headers)
+ @response.body = @body
+ end
+
+ # Decorate the response with the standard behavior of the
+ # TestResponse so that things like assert_response can be
+ # used in integration tests.
+ @response.extend(TestResponseBehavior)
+
return @status
rescue MultiPartNeededException
boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1"
@@ -347,6 +423,32 @@ module ActionController
return status
end
+ # Same as process, but handles authentication returns to perform
+ # Basic or Digest authentication
+ def process_with_auth(method, path, parameters = nil, headers = nil)
+ status = process(method, path, parameters, headers)
+
+ if status == 401
+ # Extract authentication information from response
+ auth_data = @response.headers['WWW-Authenticate']
+ if /^Basic /.match(auth_data)
+ # extract realm, to be used in subsequent request
+ @realm = auth_header.split(' ')[1]
+ elsif /^Digest/.match(auth_data)
+ creds = auth_data.to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair|
+ key, value = pair.split('=', 2)
+ hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '')
+ hash
+ end
+ @realm = creds[:realm]
+ @nonce = creds[:nonce]
+ @opaque = creds[:opaque]
+ end
+ end
+
+ return status
+ end
+
# Encode the cookies hash in a format suitable for passing to a
# request.
def encode_cookies
@@ -365,7 +467,7 @@ module ActionController
"SERVER_PORT" => https? ? "443" : "80",
"HTTPS" => https? ? "on" : "off"
}
- UrlRewriter.new(RackRequest.new(env), {})
+ UrlRewriter.new(Request.new(env), {})
end
def name_with_prefix(prefix, name)
@@ -491,8 +593,7 @@ EOF
# By default, a single session is automatically created for you, but you
# can use this method to open multiple sessions that ought to be tested
# simultaneously.
- def open_session
- application = ActionController::Dispatcher.new
+ def open_session(application = nil)
session = Integration::Session.new(application)
# delegate the fixture accessors back to the test instance
diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/layout.rb
index 54108df06d..159c5c7326 100644
--- a/actionpack/lib/action_controller/layout.rb
+++ b/actionpack/lib/action_controller/layout.rb
@@ -178,9 +178,15 @@ module ActionController #:nodoc:
find_layout(layout, format)
end
+ def layout_list #:nodoc:
+ Array(view_paths).sum([]) { |path| Dir["#{path}/layouts/**/*"] }
+ end
+
def find_layout(layout, *formats) #:nodoc:
return layout if layout.respond_to?(:render)
view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", *formats)
+ rescue ActionView::MissingTemplate
+ nil
end
private
@@ -188,7 +194,7 @@ module ActionController #:nodoc:
inherited_without_layout(child)
unless child.name.blank?
layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
- child.layout(layout_match, {}, true) if child.find_layout(layout_match, :all)
+ child.layout(layout_match, {}, true) unless child.layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
end
end
@@ -225,8 +231,16 @@ module ActionController #:nodoc:
private
def candidate_for_layout?(options)
- options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty? &&
- !@template.__send__(:_exempt_from_layout?, options[:template] || default_template_name(options[:action]))
+ template = options[:template] || default_template(options[:action])
+ if options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty?
+ begin
+ !self.view_paths.find_template(template, default_template_format).exempt_from_layout?
+ rescue ActionView::MissingTemplate
+ true
+ end
+ end
+ rescue ActionView::MissingTemplate
+ false
end
def pick_layout(options)
@@ -235,7 +249,7 @@ module ActionController #:nodoc:
when FalseClass
nil
when NilClass, TrueClass
- active_layout if action_has_layout? && !@template.__send__(:_exempt_from_layout?, default_template_name)
+ active_layout if action_has_layout? && candidate_for_layout?(:template => default_template_name)
else
active_layout(layout)
end
diff --git a/actionpack/lib/action_controller/lock.rb b/actionpack/lib/action_controller/lock.rb
new file mode 100644
index 0000000000..c50762216e
--- /dev/null
+++ b/actionpack/lib/action_controller/lock.rb
@@ -0,0 +1,16 @@
+module ActionController
+ class Lock
+ FLAG = 'rack.multithread'.freeze
+
+ def initialize(app, lock = Mutex.new)
+ @app, @lock = app, lock
+ end
+
+ def call(env)
+ old, env[FLAG] = env[FLAG], false
+ @lock.synchronize { @app.call(env) }
+ ensure
+ env[FLAG] = old
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/middleware_stack.rb b/actionpack/lib/action_controller/middleware_stack.rb
index 1864bed23a..2bccba2ba1 100644
--- a/actionpack/lib/action_controller/middleware_stack.rb
+++ b/actionpack/lib/action_controller/middleware_stack.rb
@@ -1,14 +1,47 @@
module ActionController
class MiddlewareStack < Array
class Middleware
- attr_reader :klass, :args, :block
+ def self.new(klass, *args, &block)
+ if klass.is_a?(self)
+ klass
+ else
+ super
+ end
+ end
+
+ attr_reader :args, :block
def initialize(klass, *args, &block)
- @klass = klass.is_a?(Class) ? klass : klass.to_s.constantize
+ @klass = klass
+
+ options = args.extract_options!
+ if options.has_key?(:if)
+ @conditional = options.delete(:if)
+ else
+ @conditional = true
+ end
+ args << options unless options.empty?
+
@args = args
@block = block
end
+ def klass
+ if @klass.is_a?(Class)
+ @klass
+ else
+ @klass.to_s.constantize
+ end
+ end
+
+ def active?
+ if @conditional.respond_to?(:call)
+ @conditional.call
+ else
+ @conditional
+ end
+ end
+
def ==(middleware)
case middleware
when Middleware
@@ -21,22 +54,49 @@ module ActionController
end
def inspect
- str = @klass.to_s
- @args.each { |arg| str += ", #{arg.inspect}" }
+ str = klass.to_s
+ args.each { |arg| str += ", #{arg.inspect}" }
str
end
def build(app)
- klass.new(app, *args, &block)
+ if block
+ klass.new(app, *args, &block)
+ else
+ klass.new(app, *args)
+ end
end
end
+ def initialize(*args, &block)
+ super(*args)
+ block.call(self) if block_given?
+ end
+
+ def insert(index, *objs)
+ index = self.index(index) unless index.is_a?(Integer)
+ objs = objs.map { |obj| Middleware.new(obj) }
+ super(index, *objs)
+ end
+
+ alias_method :insert_before, :insert
+
+ def insert_after(index, *objs)
+ index = self.index(index) unless index.is_a?(Integer)
+ insert(index + 1, *objs)
+ end
+
def use(*args, &block)
- push(Middleware.new(*args, &block))
+ middleware = Middleware.new(*args, &block)
+ push(middleware)
+ end
+
+ def active
+ find_all { |middleware| middleware.active? }
end
def build(app)
- reverse.inject(app) { |a, e| e.build(a) }
+ active.reverse.inject(app) { |a, e| e.build(a) }
end
end
end
diff --git a/actionpack/lib/action_controller/middlewares.rb b/actionpack/lib/action_controller/middlewares.rb
new file mode 100644
index 0000000000..793739723f
--- /dev/null
+++ b/actionpack/lib/action_controller/middlewares.rb
@@ -0,0 +1,21 @@
+use "ActionController::Lock", :if => lambda {
+ !ActionController::Base.allow_concurrency
+}
+
+use "ActionController::Failsafe"
+
+use "ActiveRecord::QueryCache", :if => lambda { defined?(ActiveRecord) }
+
+["ActionController::Session::CookieStore",
+ "ActionController::Session::MemCacheStore",
+ "ActiveRecord::SessionStore"].each do |store|
+ use(store, ActionController::Base.session_options,
+ :if => lambda {
+ if session_store = ActionController::Base.session_store
+ session_store.name == store
+ end
+ }
+ )
+end
+
+use ActionController::VerbPiggybacking
diff --git a/actionpack/lib/action_controller/mime_responds.rb b/actionpack/lib/action_controller/mime_responds.rb
index 29294476f7..b755363873 100644
--- a/actionpack/lib/action_controller/mime_responds.rb
+++ b/actionpack/lib/action_controller/mime_responds.rb
@@ -143,12 +143,27 @@ module ActionController #:nodoc:
custom(@mime_type_priority.first, &block)
end
end
+
+ def self.generate_method_for_mime(mime)
+ sym = mime.is_a?(Symbol) ? mime : mime.to_sym
+ const = sym.to_s.upcase
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{sym}(&block) # def html(&block)
+ custom(Mime::#{const}, &block) # custom(Mime::HTML, &block)
+ end # end
+ RUBY
+ end
- def method_missing(symbol, &block)
- mime_constant = symbol.to_s.upcase
+ Mime::SET.each do |mime|
+ generate_method_for_mime(mime)
+ end
- if Mime::SET.include?(Mime.const_get(mime_constant))
- custom(Mime.const_get(mime_constant), &block)
+ def method_missing(symbol, &block)
+ mime_constant = Mime.const_get(symbol.to_s.upcase)
+
+ if Mime::SET.include?(mime_constant)
+ self.class.generate_method_for_mime(mime_constant)
+ send(symbol, &block)
else
super
end
diff --git a/actionpack/lib/action_controller/mime_type.rb b/actionpack/lib/action_controller/mime_type.rb
index 6923a13f3f..017626ba27 100644
--- a/actionpack/lib/action_controller/mime_type.rb
+++ b/actionpack/lib/action_controller/mime_type.rb
@@ -176,6 +176,14 @@ module Mime
end
end
+ def =~(mime_type)
+ return false if mime_type.blank?
+ regexp = Regexp.new(Regexp.quote(mime_type.to_s))
+ (@synonyms + [ self ]).any? do |synonym|
+ synonym.to_s =~ regexp
+ end
+ end
+
# Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
# ActionController::RequestForgeryProtection.
def verify_request?
diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb
index dce50c6c3b..924d1aa6bd 100644
--- a/actionpack/lib/action_controller/polymorphic_routes.rb
+++ b/actionpack/lib/action_controller/polymorphic_routes.rb
@@ -118,13 +118,17 @@ module ActionController
%w(edit new).each do |action|
module_eval <<-EOT, __FILE__, __LINE__
- def #{action}_polymorphic_url(record_or_hash, options = {})
- polymorphic_url(record_or_hash, options.merge(:action => "#{action}"))
- end
-
- def #{action}_polymorphic_path(record_or_hash, options = {})
- polymorphic_url(record_or_hash, options.merge(:action => "#{action}", :routing_type => :path))
- end
+ def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {})
+ polymorphic_url( # polymorphic_url(
+ record_or_hash, # record_or_hash,
+ options.merge(:action => "#{action}")) # options.merge(:action => "edit"))
+ end # end
+ #
+ def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {})
+ polymorphic_url( # polymorphic_url(
+ record_or_hash, # record_or_hash,
+ options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path))
+ end # end
EOT
end
diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb
deleted file mode 100644
index 568f893c6c..0000000000
--- a/actionpack/lib/action_controller/rack_process.rb
+++ /dev/null
@@ -1,295 +0,0 @@
-require 'action_controller/cgi_ext'
-
-module ActionController #:nodoc:
- class RackRequest < AbstractRequest #:nodoc:
- attr_accessor :session_options
- attr_reader :cgi
-
- class SessionFixationAttempt < StandardError #:nodoc:
- end
-
- DEFAULT_SESSION_OPTIONS = {
- :database_manager => CGI::Session::CookieStore, # store data in cookie
- :prefix => "ruby_sess.", # prefix session file names
- :session_path => "/", # available to all paths in app
- :session_key => "_session_id",
- :cookie_only => true,
- :session_http_only=> true
- }
-
- def initialize(env, session_options = DEFAULT_SESSION_OPTIONS)
- @session_options = session_options
- @env = env
- @cgi = CGIWrapper.new(self)
- super()
- end
-
- %w[ AUTH_TYPE GATEWAY_INTERFACE PATH_INFO
- PATH_TRANSLATED REMOTE_HOST
- REMOTE_IDENT REMOTE_USER SCRIPT_NAME
- SERVER_NAME SERVER_PROTOCOL
-
- HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
- HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
- HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
- define_method(env.sub(/^HTTP_/n, '').downcase) do
- @env[env]
- end
- end
-
- def query_string
- qs = super
- if !qs.blank?
- qs
- else
- @env['QUERY_STRING']
- end
- end
-
- def body_stream #:nodoc:
- @env['rack.input']
- end
-
- def key?(key)
- @env.key?(key)
- end
-
- def cookies
- Rack::Request.new(@env).cookies
- end
-
- def server_port
- @env['SERVER_PORT'].to_i
- end
-
- def server_software
- @env['SERVER_SOFTWARE'].split("/").first
- end
-
- def session
- unless defined?(@session)
- if @session_options == false
- @session = Hash.new
- else
- stale_session_check! do
- if cookie_only? && query_parameters[session_options_with_string_keys['session_key']]
- raise SessionFixationAttempt
- end
- case value = session_options_with_string_keys['new_session']
- when true
- @session = new_session
- when false
- begin
- @session = CGI::Session.new(@cgi, session_options_with_string_keys)
- # CGI::Session raises ArgumentError if 'new_session' == false
- # and no session cookie or query param is present.
- rescue ArgumentError
- @session = Hash.new
- end
- when nil
- @session = CGI::Session.new(@cgi, session_options_with_string_keys)
- else
- raise ArgumentError, "Invalid new_session option: #{value}"
- end
- @session['__valid_session']
- end
- end
- end
- @session
- end
-
- def reset_session
- @session.delete if defined?(@session) && @session.is_a?(CGI::Session)
- @session = new_session
- end
-
- private
- # Delete an old session if it exists then create a new one.
- def new_session
- if @session_options == false
- Hash.new
- else
- CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil
- CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true))
- end
- end
-
- def cookie_only?
- session_options_with_string_keys['cookie_only']
- end
-
- def stale_session_check!
- yield
- rescue ArgumentError => argument_error
- if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
- begin
- # Note that the regexp does not allow $1 to end with a ':'
- $1.constantize
- rescue LoadError, NameError => const_error
- raise ActionController::SessionRestoreError, <<-end_msg
-Session contains objects whose class definition isn\'t available.
-Remember to require the classes for all objects kept in the session.
-(Original exception: #{const_error.message} [#{const_error.class}])
-end_msg
- end
-
- retry
- else
- raise
- end
- end
-
- def session_options_with_string_keys
- @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys
- end
- end
-
- class RackResponse < AbstractResponse #:nodoc:
- def initialize(request)
- @cgi = request.cgi
- @writer = lambda { |x| @body << x }
- @block = nil
- super()
- end
-
- # Retrieve status from instance variable if has already been delete
- def status
- @status || super
- end
-
- def out(&block)
- # Nasty hack because CGI sessions are closed after the normal
- # prepare! statement
- set_cookies!
-
- @block = block
- @status = headers.delete("Status")
- if [204, 304].include?(status.to_i)
- headers.delete("Content-Type")
- [status, headers.to_hash, []]
- else
- [status, headers.to_hash, self]
- end
- end
- alias to_a out
-
- def each(&callback)
- if @body.respond_to?(:call)
- @writer = lambda { |x| callback.call(x) }
- @body.call(self, self)
- elsif @body.is_a?(String)
- @body.each_line(&callback)
- else
- @body.each(&callback)
- end
-
- @writer = callback
- @block.call(self) if @block
- end
-
- def write(str)
- @writer.call str.to_s
- str
- end
-
- def close
- @body.close if @body.respond_to?(:close)
- end
-
- def empty?
- @block == nil && @body.empty?
- end
-
- def prepare!
- super
-
- convert_language!
- convert_expires!
- set_status!
- # set_cookies!
- end
-
- private
- def convert_language!
- headers["Content-Language"] = headers.delete("language") if headers["language"]
- end
-
- def convert_expires!
- headers["Expires"] = headers.delete("") if headers["expires"]
- end
-
- def convert_content_type!
- super
- headers['Content-Type'] = headers.delete('type') || "text/html"
- headers['Content-Type'] += "; charset=" + headers.delete('charset') if headers['charset']
- end
-
- def set_content_length!
- super
- headers["Content-Length"] = headers["Content-Length"].to_s if headers["Content-Length"]
- end
-
- def set_status!
- self.status ||= "200 OK"
- end
-
- def set_cookies!
- # Convert 'cookie' header to 'Set-Cookie' headers.
- # Because Set-Cookie header can appear more the once in the response body,
- # we store it in a line break separated string that will be translated to
- # multiple Set-Cookie header by the handler.
- if cookie = headers.delete('cookie')
- cookies = []
-
- case cookie
- when Array then cookie.each { |c| cookies << c.to_s }
- when Hash then cookie.each { |_, c| cookies << c.to_s }
- else cookies << cookie.to_s
- end
-
- @cgi.output_cookies.each { |c| cookies << c.to_s } if @cgi.output_cookies
-
- headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].flatten.compact
- end
- end
- end
-
- class CGIWrapper < ::CGI
- attr_reader :output_cookies
-
- def initialize(request, *args)
- @request = request
- @args = *args
- @input = request.body
-
- super *args
- end
-
- def params
- @params ||= @request.params
- end
-
- def cookies
- @request.cookies
- end
-
- def query_string
- @request.query_string
- end
-
- # Used to wrap the normal args variable used inside CGI.
- def args
- @args
- end
-
- # Used to wrap the normal env_table variable used inside CGI.
- def env_table
- @request.env
- end
-
- # Used to wrap the normal stdinput variable used inside CGI.
- def stdinput
- @input
- end
- end
-end
diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb
index 087fffe87d..b4ab1ccda1 100755
--- a/actionpack/lib/action_controller/request.rb
+++ b/actionpack/lib/action_controller/request.rb
@@ -3,33 +3,41 @@ require 'stringio'
require 'strscan'
require 'active_support/memoizable'
+require 'action_controller/cgi_ext'
module ActionController
- # CgiRequest and TestRequest provide concrete implementations.
- class AbstractRequest
+ class Request < Rack::Request
extend ActiveSupport::Memoizable
- def self.relative_url_root=(relative_url_root)
- ActiveSupport::Deprecation.warn(
- "ActionController::AbstractRequest.relative_url_root= has been renamed." +
- "You can now set it with config.action_controller.relative_url_root=", caller)
- ActionController::Base.relative_url_root=relative_url_root
+ def initialize(env)
+ super
+ @parser = ActionController::RequestParser.new(env)
+ end
+
+ %w[ AUTH_TYPE GATEWAY_INTERFACE
+ PATH_TRANSLATED REMOTE_HOST
+ REMOTE_IDENT REMOTE_USER REMOTE_ADDR
+ SERVER_NAME SERVER_PROTOCOL
+
+ HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
+ HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
+ HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
+ define_method(env.sub(/^HTTP_/n, '').downcase) do
+ @env[env]
+ end
+ end
+
+ def key?(key)
+ @env.key?(key)
end
HTTP_METHODS = %w(get head put post delete options)
HTTP_METHOD_LOOKUP = HTTP_METHODS.inject({}) { |h, m| h[m] = h[m.upcase] = m.to_sym; h }
- # The hash of environment variables for this request,
- # such as { 'RAILS_ENV' => 'production' }.
- attr_reader :env
-
# The true HTTP request \method as a lowercase symbol, such as <tt>:get</tt>.
# UnknownHttpMethod is raised for invalid methods not listed in ACCEPTED_HTTP_METHODS.
def request_method
- method = @env['REQUEST_METHOD']
- method = parameters[:_method] if method == 'POST' && !parameters[:_method].blank?
-
- HTTP_METHOD_LOOKUP[method] || raise(UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}")
+ HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}")
end
memoize :request_method
@@ -76,16 +84,15 @@ module ActionController
# Returns the content length of the request as an integer.
def content_length
- @env['CONTENT_LENGTH'].to_i
+ super.to_i
end
- memoize :content_length
# The MIME type of the HTTP request, such as Mime::XML.
#
# For backward compatibility, the post \format is extracted from the
# X-Post-Data-Format HTTP header if present.
def content_type
- Mime::Type.lookup(content_type_without_parameters)
+ Mime::Type.lookup(@parser.content_type_without_parameters)
end
memoize :content_type
@@ -125,15 +132,15 @@ module ActionController
# supplied, both must match, or the request is not considered fresh.
def fresh?(response)
case
- when if_modified_since && if_none_match
- not_modified?(response.last_modified) && etag_matches?(response.etag)
- when if_modified_since
- not_modified?(response.last_modified)
- when if_none_match
- etag_matches?(response.etag)
- else
- false
- end
+ when if_modified_since && if_none_match
+ not_modified?(response.last_modified) && etag_matches?(response.etag)
+ when if_modified_since
+ not_modified?(response.last_modified)
+ when if_none_match
+ etag_matches?(response.etag)
+ else
+ false
+ end
end
# Returns the Mime type for the \format used in the request.
@@ -248,7 +255,6 @@ EOM
end
memoize :server_software
-
# Returns the complete URL used for this request.
def url
protocol + host_with_port + request_uri
@@ -271,7 +277,7 @@ EOM
if forwarded = env["HTTP_X_FORWARDED_HOST"]
forwarded.split(/,\s?/).last
else
- env['HTTP_HOST'] || env['SERVER_NAME'] || "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
+ env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
end
end
@@ -332,11 +338,7 @@ EOM
# Returns the query string, accounting for server idiosyncrasies.
def query_string
- if uri = @env['REQUEST_URI']
- uri.split('?', 2)[1] || ''
- else
- @env['QUERY_STRING'] || ''
- end
+ @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].split('?', 2)[1] || '')
end
memoize :query_string
@@ -378,20 +380,17 @@ EOM
# Read the request \body. This is useful for web services that need to
# work with raw requests directly.
def raw_post
- unless env.include? 'RAW_POST_DATA'
- env['RAW_POST_DATA'] = body.read(content_length)
- body.rewind if body.respond_to?(:rewind)
- end
- env['RAW_POST_DATA']
+ @parser.raw_post
end
# Returns both GET and POST \parameters in a single hash.
def parameters
@parameters ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access
end
+ alias_method :params, :parameters
def path_parameters=(parameters) #:nodoc:
- @path_parameters = parameters
+ @env["rack.routing_args"] = parameters
@symbolized_path_parameters = @parameters = nil
end
@@ -407,464 +406,56 @@ EOM
#
# See <tt>symbolized_path_parameters</tt> for symbolized keys.
def path_parameters
- @path_parameters ||= {}
+ @env["rack.routing_args"] ||= {}
end
- # The request body is an IO input stream. If the RAW_POST_DATA environment
- # variable is already set, wrap it in a StringIO.
def body
- if raw_post = env['RAW_POST_DATA']
- raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
- StringIO.new(raw_post)
- else
- body_stream
- end
- end
-
- def remote_addr
- @env['REMOTE_ADDR']
- end
-
- def referrer
- @env['HTTP_REFERER']
+ @parser.body
end
- alias referer referrer
-
- def query_parameters
- @query_parameters ||= self.class.parse_query_parameters(query_string)
+ # Override Rack's GET method to support nested query strings
+ def GET
+ @parser.query_parameters
end
+ alias_method :query_parameters, :GET
- def request_parameters
- @request_parameters ||= parse_formatted_request_parameters
+ # Override Rack's POST method to support nested query strings
+ def POST
+ @parser.request_parameters
end
-
-
- #--
- # Must be implemented in the concrete request
- #++
+ alias_method :request_parameters, :POST
def body_stream #:nodoc:
+ @env['rack.input']
end
- def cookies #:nodoc:
- end
-
- def session #:nodoc:
+ def session
+ @env['rack.session'] ||= {}
end
def session=(session) #:nodoc:
- @session = session
+ @env['rack.session'] = session
end
- def reset_session #:nodoc:
+ def reset_session
+ @env['rack.session'] = {}
end
- protected
- # The raw content type string. Use when you need parameters such as
- # charset or boundary which aren't included in the content_type MIME type.
- # Overridden by the X-POST_DATA_FORMAT header for backward compatibility.
- def content_type_with_parameters
- content_type_from_legacy_post_data_format_header ||
- env['CONTENT_TYPE'].to_s
- end
-
- # The raw content type string with its parameters stripped off.
- def content_type_without_parameters
- self.class.extract_content_type_without_parameters(content_type_with_parameters)
- end
- memoize :content_type_without_parameters
-
- private
- def content_type_from_legacy_post_data_format_header
- if x_post_format = @env['HTTP_X_POST_DATA_FORMAT']
- case x_post_format.to_s.downcase
- when 'yaml'; 'application/x-yaml'
- when 'xml'; 'application/xml'
- end
- end
- end
-
- def parse_formatted_request_parameters
- return {} if content_length.zero?
-
- content_type, boundary = self.class.extract_multipart_boundary(content_type_with_parameters)
-
- # Don't parse params for unknown requests.
- return {} if content_type.blank?
-
- mime_type = Mime::Type.lookup(content_type)
- strategy = ActionController::Base.param_parsers[mime_type]
-
- # Only multipart form parsing expects a stream.
- body = (strategy && strategy != :multipart_form) ? raw_post : self.body
-
- case strategy
- when Proc
- strategy.call(body)
- when :url_encoded_form
- self.class.clean_up_ajax_request_body! body
- self.class.parse_query_parameters(body)
- when :multipart_form
- self.class.parse_multipart_form_parameters(body, boundary, content_length, env)
- when :xml_simple, :xml_node
- body.blank? ? {} : Hash.from_xml(body).with_indifferent_access
- when :yaml
- YAML.load(body)
- when :json
- if body.blank?
- {}
- else
- data = ActiveSupport::JSON.decode(body)
- data = {:_json => data} unless data.is_a?(Hash)
- data.with_indifferent_access
- end
- else
- {}
- end
- rescue Exception => e # YAML, XML or Ruby code block errors
- raise
- { "body" => body,
- "content_type" => content_type_with_parameters,
- "content_length" => content_length,
- "exception" => "#{e.message} (#{e.class})",
- "backtrace" => e.backtrace }
- end
-
- def named_host?(host)
- !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
- end
-
- class << self
- def parse_query_parameters(query_string)
- return {} if query_string.blank?
-
- pairs = query_string.split('&').collect do |chunk|
- next if chunk.empty?
- key, value = chunk.split('=', 2)
- next if key.empty?
- value = value.nil? ? nil : CGI.unescape(value)
- [ CGI.unescape(key), value ]
- end.compact
-
- UrlEncodedPairParser.new(pairs).result
- end
-
- def parse_request_parameters(params)
- parser = UrlEncodedPairParser.new
-
- params = params.dup
- until params.empty?
- for key, value in params
- if key.blank?
- params.delete key
- elsif !key.include?('[')
- # much faster to test for the most common case first (GET)
- # and avoid the call to build_deep_hash
- parser.result[key] = get_typed_value(value[0])
- params.delete key
- elsif value.is_a?(Array)
- parser.parse(key, get_typed_value(value.shift))
- params.delete key if value.empty?
- else
- raise TypeError, "Expected array, found #{value.inspect}"
- end
- end
- end
-
- parser.result
- end
-
- def parse_multipart_form_parameters(body, boundary, body_size, env)
- parse_request_parameters(read_multipart(body, boundary, body_size, env))
- end
-
- def extract_multipart_boundary(content_type_with_parameters)
- if content_type_with_parameters =~ MULTIPART_BOUNDARY
- ['multipart/form-data', $1.dup]
- else
- extract_content_type_without_parameters(content_type_with_parameters)
- end
- end
-
- def extract_content_type_without_parameters(content_type_with_parameters)
- $1.strip.downcase if content_type_with_parameters =~ /^([^,\;]*)/
- end
-
- def clean_up_ajax_request_body!(body)
- body.chop! if body[-1] == 0
- body.gsub!(/&_=$/, '')
- end
-
-
- private
- def get_typed_value(value)
- case value
- when String
- value
- when NilClass
- ''
- when Array
- value.map { |v| get_typed_value(v) }
- else
- if value.respond_to? :original_filename
- # Uploaded file
- if value.original_filename
- value
- # Multipart param
- else
- result = value.read
- value.rewind
- result
- end
- # Unknown value, neither string nor multipart.
- else
- raise "Unknown form value: #{value.inspect}"
- end
- end
- end
-
- MULTIPART_BOUNDARY = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
-
- EOL = "\015\012"
-
- def read_multipart(body, boundary, body_size, env)
- params = Hash.new([])
- boundary = "--" + boundary
- quoted_boundary = Regexp.quote(boundary)
- buf = ""
- bufsize = 10 * 1024
- boundary_end=""
-
- # start multipart/form-data
- body.binmode if defined? body.binmode
- case body
- when File
- body.set_encoding(Encoding::BINARY) if body.respond_to?(:set_encoding)
- when StringIO
- body.string.force_encoding(Encoding::BINARY) if body.string.respond_to?(:force_encoding)
- end
- boundary_size = boundary.size + EOL.size
- body_size -= boundary_size
- status = body.read(boundary_size)
- if nil == status
- raise EOFError, "no content body"
- elsif boundary + EOL != status
- raise EOFError, "bad content body"
- end
-
- loop do
- head = nil
- content =
- if 10240 < body_size
- UploadedTempfile.new("CGI")
- else
- UploadedStringIO.new
- end
- content.binmode if defined? content.binmode
-
- until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
-
- if (not head) and /#{EOL}#{EOL}/n.match(buf)
- buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
- head = $1.dup
- ""
- end
- next
- end
-
- if head and ( (EOL + boundary + EOL).size < buf.size )
- content.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
- buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
- end
-
- c = if bufsize < body_size
- body.read(bufsize)
- else
- body.read(body_size)
- end
- if c.nil? || c.empty?
- raise EOFError, "bad content body"
- end
- buf.concat(c)
- body_size -= c.size
- end
-
- buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
- content.print $1
- if "--" == $2
- body_size = -1
- end
- boundary_end = $2.dup
- ""
- end
-
- content.rewind
-
- head =~ /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni
- if filename = $1 || $2
- if /Mac/ni.match(env['HTTP_USER_AGENT']) and
- /Mozilla/ni.match(env['HTTP_USER_AGENT']) and
- (not /MSIE/ni.match(env['HTTP_USER_AGENT']))
- filename = CGI.unescape(filename)
- end
- content.original_path = filename.dup
- end
-
- head =~ /Content-Type: ([^\r]*)/ni
- content.content_type = $1.dup if $1
-
- head =~ /Content-Disposition:.* name="?([^\";]*)"?/ni
- name = $1.dup if $1
-
- if params.has_key?(name)
- params[name].push(content)
- else
- params[name] = [content]
- end
- break if body_size == -1
- end
- raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
-
- begin
- body.rewind if body.respond_to?(:rewind)
- rescue Errno::ESPIPE
- # Handles exceptions raised by input streams that cannot be rewound
- # such as when using plain CGI under Apache
- end
-
- params
- end
+ def session_options
+ @env['rack.session.options'] ||= {}
end
- end
-
- class UrlEncodedPairParser < StringScanner #:nodoc:
- attr_reader :top, :parent, :result
- def initialize(pairs = [])
- super('')
- @result = {}
- pairs.each { |key, value| parse(key, value) }
+ def session_options=(options)
+ @env['rack.session.options'] = options
end
- KEY_REGEXP = %r{([^\[\]=&]+)}
- BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]}
-
- # Parse the query string
- def parse(key, value)
- self.string = key
- @top, @parent = result, nil
-
- # First scan the bare key
- key = scan(KEY_REGEXP) or return
- key = post_key_check(key)
-
- # Then scan as many nestings as present
- until eos?
- r = scan(BRACKETED_KEY_REGEXP) or return
- key = self[1]
- key = post_key_check(key)
- end
-
- bind(key, value)
+ def server_port
+ @env['SERVER_PORT'].to_i
end
private
- # After we see a key, we must look ahead to determine our next action. Cases:
- #
- # [] follows the key. Then the value must be an array.
- # = follows the key. (A value comes next)
- # & or the end of string follows the key. Then the key is a flag.
- # otherwise, a hash follows the key.
- def post_key_check(key)
- if scan(/\[\]/) # a[b][] indicates that b is an array
- container(key, Array)
- nil
- elsif check(/\[[^\]]/) # a[b] indicates that a is a hash
- container(key, Hash)
- nil
- else # End of key? We do nothing.
- key
- end
- end
-
- # Add a container to the stack.
- def container(key, klass)
- type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass)
- value = bind(key, klass.new)
- type_conflict! klass, value unless value.is_a?(klass)
- push(value)
- end
-
- # Push a value onto the 'stack', which is actually only the top 2 items.
- def push(value)
- @parent, @top = @top, value
- end
-
- # Bind a key (which may be nil for items in an array) to the provided value.
- def bind(key, value)
- if top.is_a? Array
- if key
- if top[-1].is_a?(Hash) && ! top[-1].key?(key)
- top[-1][key] = value
- else
- top << {key => value}.with_indifferent_access
- push top.last
- value = top[key]
- end
- else
- top << value
- end
- elsif top.is_a? Hash
- key = CGI.unescape(key)
- parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
- top[key] ||= value
- return top[key]
- else
- raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
- end
-
- return value
- end
-
- def type_conflict!(klass, value)
- raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)"
- end
- end
-
- module UploadedFile
- def self.included(base)
- base.class_eval do
- attr_accessor :original_path, :content_type
- alias_method :local_path, :path
- end
- end
-
- # Take the basename of the upload's original filename.
- # This handles the full Windows paths given by Internet Explorer
- # (and perhaps other broken user agents) without affecting
- # those which give the lone filename.
- # The Windows regexp is adapted from Perl's File::Basename.
- def original_filename
- unless defined? @original_filename
- @original_filename =
- unless original_path.blank?
- if original_path =~ /^(?:.*[:\\\/])?(.*)/m
- $1
- else
- File.basename original_path
- end
- end
+ def named_host?(host)
+ !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
end
- @original_filename
- end
- end
-
- class UploadedStringIO < StringIO
- include UploadedFile
- end
-
- class UploadedTempfile < Tempfile
- include UploadedFile
end
end
diff --git a/actionpack/lib/action_controller/request_parser.rb b/actionpack/lib/action_controller/request_parser.rb
new file mode 100644
index 0000000000..d1739ef4d0
--- /dev/null
+++ b/actionpack/lib/action_controller/request_parser.rb
@@ -0,0 +1,315 @@
+module ActionController
+ class RequestParser
+ def initialize(env)
+ @env = env
+ freeze
+ end
+
+ def request_parameters
+ @env["action_controller.request_parser.request_parameters"] ||= parse_formatted_request_parameters
+ end
+
+ def query_parameters
+ @env["action_controller.request_parser.query_parameters"] ||= self.class.parse_query_parameters(query_string)
+ end
+
+ # Returns the query string, accounting for server idiosyncrasies.
+ def query_string
+ @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].split('?', 2)[1] || '')
+ end
+
+ # The request body is an IO input stream. If the RAW_POST_DATA environment
+ # variable is already set, wrap it in a StringIO.
+ def body
+ if raw_post = @env['RAW_POST_DATA']
+ raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
+ StringIO.new(raw_post)
+ else
+ @env['rack.input']
+ end
+ end
+
+ # The raw content type string with its parameters stripped off.
+ def content_type_without_parameters
+ self.class.extract_content_type_without_parameters(content_type_with_parameters)
+ end
+
+ def raw_post
+ unless @env.include? 'RAW_POST_DATA'
+ @env['RAW_POST_DATA'] = body.read(content_length)
+ body.rewind if body.respond_to?(:rewind)
+ end
+ @env['RAW_POST_DATA']
+ end
+
+ private
+
+ def parse_formatted_request_parameters
+ return {} if content_length.zero?
+
+ content_type, boundary = self.class.extract_multipart_boundary(content_type_with_parameters)
+
+ # Don't parse params for unknown requests.
+ return {} if content_type.blank?
+
+ mime_type = Mime::Type.lookup(content_type)
+ strategy = ActionController::Base.param_parsers[mime_type]
+
+ # Only multipart form parsing expects a stream.
+ body = (strategy && strategy != :multipart_form) ? raw_post : self.body
+
+ case strategy
+ when Proc
+ strategy.call(body)
+ when :url_encoded_form
+ self.class.clean_up_ajax_request_body! body
+ self.class.parse_query_parameters(body)
+ when :multipart_form
+ self.class.parse_multipart_form_parameters(body, boundary, content_length, @env)
+ when :xml_simple, :xml_node
+ body.blank? ? {} : Hash.from_xml(body).with_indifferent_access
+ when :yaml
+ YAML.load(body)
+ when :json
+ if body.blank?
+ {}
+ else
+ data = ActiveSupport::JSON.decode(body)
+ data = {:_json => data} unless data.is_a?(Hash)
+ data.with_indifferent_access
+ end
+ else
+ {}
+ end
+ rescue Exception => e # YAML, XML or Ruby code block errors
+ raise
+ { "body" => body,
+ "content_type" => content_type_with_parameters,
+ "content_length" => content_length,
+ "exception" => "#{e.message} (#{e.class})",
+ "backtrace" => e.backtrace }
+ end
+
+ def content_length
+ @env['CONTENT_LENGTH'].to_i
+ end
+
+ # The raw content type string. Use when you need parameters such as
+ # charset or boundary which aren't included in the content_type MIME type.
+ # Overridden by the X-POST_DATA_FORMAT header for backward compatibility.
+ def content_type_with_parameters
+ content_type_from_legacy_post_data_format_header || @env['CONTENT_TYPE'].to_s
+ end
+
+ def content_type_from_legacy_post_data_format_header
+ if x_post_format = @env['HTTP_X_POST_DATA_FORMAT']
+ case x_post_format.to_s.downcase
+ when 'yaml'; 'application/x-yaml'
+ when 'xml'; 'application/xml'
+ end
+ end
+ end
+
+ class << self
+ def parse_query_parameters(query_string)
+ return {} if query_string.blank?
+
+ pairs = query_string.split('&').collect do |chunk|
+ next if chunk.empty?
+ key, value = chunk.split('=', 2)
+ next if key.empty?
+ value = value.nil? ? nil : CGI.unescape(value)
+ [ CGI.unescape(key), value ]
+ end.compact
+
+ UrlEncodedPairParser.new(pairs).result
+ end
+
+ def parse_request_parameters(params)
+ parser = UrlEncodedPairParser.new
+
+ params = params.dup
+ until params.empty?
+ for key, value in params
+ if key.blank?
+ params.delete key
+ elsif !key.include?('[')
+ # much faster to test for the most common case first (GET)
+ # and avoid the call to build_deep_hash
+ parser.result[key] = get_typed_value(value[0])
+ params.delete key
+ elsif value.is_a?(Array)
+ parser.parse(key, get_typed_value(value.shift))
+ params.delete key if value.empty?
+ else
+ raise TypeError, "Expected array, found #{value.inspect}"
+ end
+ end
+ end
+
+ parser.result
+ end
+
+ def parse_multipart_form_parameters(body, boundary, body_size, env)
+ parse_request_parameters(read_multipart(body, boundary, body_size, env))
+ end
+
+ def extract_multipart_boundary(content_type_with_parameters)
+ if content_type_with_parameters =~ MULTIPART_BOUNDARY
+ ['multipart/form-data', $1.dup]
+ else
+ extract_content_type_without_parameters(content_type_with_parameters)
+ end
+ end
+
+ def extract_content_type_without_parameters(content_type_with_parameters)
+ $1.strip.downcase if content_type_with_parameters =~ /^([^,\;]*)/
+ end
+
+ def clean_up_ajax_request_body!(body)
+ body.chop! if body[-1] == 0
+ body.gsub!(/&_=$/, '')
+ end
+
+
+ private
+ def get_typed_value(value)
+ case value
+ when String
+ value
+ when NilClass
+ ''
+ when Array
+ value.map { |v| get_typed_value(v) }
+ else
+ if value.respond_to? :original_filename
+ # Uploaded file
+ if value.original_filename
+ value
+ # Multipart param
+ else
+ result = value.read
+ value.rewind
+ result
+ end
+ # Unknown value, neither string nor multipart.
+ else
+ raise "Unknown form value: #{value.inspect}"
+ end
+ end
+ end
+
+ MULTIPART_BOUNDARY = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
+
+ EOL = "\015\012"
+
+ def read_multipart(body, boundary, body_size, env)
+ params = Hash.new([])
+ boundary = "--" + boundary
+ quoted_boundary = Regexp.quote(boundary)
+ buf = ""
+ bufsize = 10 * 1024
+ boundary_end=""
+
+ # start multipart/form-data
+ body.binmode if defined? body.binmode
+ case body
+ when File
+ body.set_encoding(Encoding::BINARY) if body.respond_to?(:set_encoding)
+ when StringIO
+ body.string.force_encoding(Encoding::BINARY) if body.string.respond_to?(:force_encoding)
+ end
+ boundary_size = boundary.size + EOL.size
+ body_size -= boundary_size
+ status = body.read(boundary_size)
+ if nil == status
+ raise EOFError, "no content body"
+ elsif boundary + EOL != status
+ raise EOFError, "bad content body"
+ end
+
+ loop do
+ head = nil
+ content =
+ if 10240 < body_size
+ UploadedTempfile.new("CGI")
+ else
+ UploadedStringIO.new
+ end
+ content.binmode if defined? content.binmode
+
+ until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
+
+ if (not head) and /#{EOL}#{EOL}/n.match(buf)
+ buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
+ head = $1.dup
+ ""
+ end
+ next
+ end
+
+ if head and ( (EOL + boundary + EOL).size < buf.size )
+ content.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
+ buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
+ end
+
+ c = if bufsize < body_size
+ body.read(bufsize)
+ else
+ body.read(body_size)
+ end
+ if c.nil? || c.empty?
+ raise EOFError, "bad content body"
+ end
+ buf.concat(c)
+ body_size -= c.size
+ end
+
+ buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
+ content.print $1
+ if "--" == $2
+ body_size = -1
+ end
+ boundary_end = $2.dup
+ ""
+ end
+
+ content.rewind
+
+ head =~ /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni
+ if filename = $1 || $2
+ if /Mac/ni.match(env['HTTP_USER_AGENT']) and
+ /Mozilla/ni.match(env['HTTP_USER_AGENT']) and
+ (not /MSIE/ni.match(env['HTTP_USER_AGENT']))
+ filename = CGI.unescape(filename)
+ end
+ content.original_path = filename.dup
+ end
+
+ head =~ /Content-Type: ([^\r]*)/ni
+ content.content_type = $1.dup if $1
+
+ head =~ /Content-Disposition:.* name="?([^\";]*)"?/ni
+ name = $1.dup if $1
+
+ if params.has_key?(name)
+ params[name].push(content)
+ else
+ params[name] = [content]
+ end
+ break if body_size == -1
+ end
+ raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
+
+ begin
+ body.rewind if body.respond_to?(:rewind)
+ rescue Errno::ESPIPE
+ # Handles exceptions raised by input streams that cannot be rewound
+ # such as when using plain CGI under Apache
+ end
+
+ params
+ end
+ end # class << self
+ end
+end
diff --git a/actionpack/lib/action_controller/request_profiler.rb b/actionpack/lib/action_controller/request_profiler.rb
index 70bb77e7ac..80cd55334f 100644
--- a/actionpack/lib/action_controller/request_profiler.rb
+++ b/actionpack/lib/action_controller/request_profiler.rb
@@ -20,7 +20,7 @@ module ActionController
@quiet = true
print ' '
- result = Benchmark.realtime do
+ ms = Benchmark.ms do
n.times do |i|
run(profiling)
print_progress(i)
@@ -28,7 +28,7 @@ module ActionController
end
puts
- result
+ ms
ensure
@quiet = false
end
@@ -88,7 +88,7 @@ module ActionController
puts 'Warming up once'
elapsed = warmup(sandbox)
- puts '%.2f sec, %d requests, %d req/sec' % [elapsed, sandbox.request_count, sandbox.request_count / elapsed]
+ puts '%.0f ms, %d requests, %d req/sec' % [elapsed, sandbox.request_count, 1000 * sandbox.request_count / elapsed]
puts "\n#{options[:benchmark] ? 'Benchmarking' : 'Profiling'} #{options[:n]}x"
options[:benchmark] ? benchmark(sandbox) : profile(sandbox)
@@ -106,13 +106,13 @@ module ActionController
def benchmark(sandbox, profiling = false)
sandbox.request_count = 0
- elapsed = sandbox.benchmark(options[:n], profiling).to_f
+ elapsed = sandbox.benchmark(options[:n], profiling)
count = sandbox.request_count.to_i
- puts '%.2f sec, %d requests, %d req/sec' % [elapsed, count, count / elapsed]
+ puts '%.0f ms, %d requests, %d req/sec' % [elapsed, count, 1000 * count / elapsed]
end
def warmup(sandbox)
- Benchmark.realtime { sandbox.run(false) }
+ Benchmark.ms { sandbox.run(false) }
end
def default_options
diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb
index d7b0e96c93..4b7d1e32fd 100644
--- a/actionpack/lib/action_controller/rescue.rb
+++ b/actionpack/lib/action_controller/rescue.rb
@@ -38,8 +38,8 @@ module ActionController #:nodoc:
'ActionView::TemplateError' => 'template_error'
}
- RESCUES_TEMPLATE_PATH = ActionView::PathSet::Path.new(
- "#{File.dirname(__FILE__)}/templates", true)
+ RESCUES_TEMPLATE_PATH = ActionView::Template::EagerPath.new(
+ File.join(File.dirname(__FILE__), "templates"))
def self.included(base) #:nodoc:
base.cattr_accessor :rescue_responses
@@ -59,7 +59,9 @@ module ActionController #:nodoc:
end
module ClassMethods
- def process_with_exception(request, response, exception) #:nodoc:
+ def call_with_exception(env, exception) #:nodoc:
+ request = env["action_controller.rescue.request"] ||= Request.new(env)
+ response = env["action_controller.rescue.response"] ||= Response.new
new.process(request, response, :rescue_action, exception)
end
end
@@ -102,9 +104,9 @@ module ActionController #:nodoc:
# doesn't exist, the body of the response will be left empty.
def render_optional_error_file(status_code)
status = interpret_status(status_code)
- path = "#{Rails.public_path}/#{status[0,3]}.html"
+ path = "#{Rails.public_path}/#{status.to_s[0,3]}.html"
if File.exist?(path)
- render :file => path, :status => status
+ render :file => path, :status => status, :content_type => Mime::HTML
else
head status
end
diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb
index 559c38efd0..27860a6207 100644
--- a/actionpack/lib/action_controller/response.rb
+++ b/actionpack/lib/action_controller/response.rb
@@ -1,24 +1,25 @@
require 'digest/md5'
module ActionController # :nodoc:
- # Represents an HTTP response generated by a controller action. One can use an
- # ActionController::AbstractResponse object to retrieve the current state of the
- # response, or customize the response. An AbstractResponse object can either
- # represent a "real" HTTP response (i.e. one that is meant to be sent back to the
- # web browser) or a test response (i.e. one that is generated from integration
- # tests). See CgiResponse and TestResponse, respectively.
+ # Represents an HTTP response generated by a controller action. One can use
+ # an ActionController::Response object to retrieve the current state
+ # of the response, or customize the response. An Response object can
+ # either represent a "real" HTTP response (i.e. one that is meant to be sent
+ # back to the web browser) or a test response (i.e. one that is generated
+ # from integration tests). See CgiResponse and TestResponse, respectively.
#
- # AbstractResponse is mostly a Ruby on Rails framework implement detail, and should
- # never be used directly in controllers. Controllers should use the methods defined
- # in ActionController::Base instead. For example, if you want to set the HTTP
- # response's content MIME type, then use ActionControllerBase#headers instead of
- # AbstractResponse#headers.
+ # Response is mostly a Ruby on Rails framework implement detail, and
+ # should never be used directly in controllers. Controllers should use the
+ # methods defined in ActionController::Base instead. For example, if you want
+ # to set the HTTP response's content MIME type, then use
+ # ActionControllerBase#headers instead of Response#headers.
#
- # Nevertheless, integration tests may want to inspect controller responses in more
- # detail, and that's when AbstractResponse can be useful for application developers.
- # Integration test methods such as ActionController::Integration::Session#get and
- # ActionController::Integration::Session#post return objects of type TestResponse
- # (which are of course also of type AbstractResponse).
+ # Nevertheless, integration tests may want to inspect controller responses in
+ # more detail, and that's when Response can be useful for application
+ # developers. Integration test methods such as
+ # ActionController::Integration::Session#get and
+ # ActionController::Integration::Session#post return objects of type
+ # TestResponse (which are of course also of type Response).
#
# For example, the following demo integration "test" prints the body of the
# controller response to the console:
@@ -29,25 +30,25 @@ module ActionController # :nodoc:
# puts @response.body
# end
# end
- class AbstractResponse
+ class Response < Rack::Response
DEFAULT_HEADERS = { "Cache-Control" => "no-cache" }
attr_accessor :request
- # The body content (e.g. HTML) of the response, as a String.
- attr_accessor :body
- # The headers of the response, as a Hash. It maps header names to header values.
- attr_accessor :headers
- attr_accessor :session, :cookies, :assigns, :template, :layout
+ attr_accessor :session, :assigns, :template, :layout
attr_accessor :redirected_to, :redirected_to_method_params
delegate :default_charset, :to => 'ActionController::Base'
def initialize
- @body, @headers, @session, @assigns = "", DEFAULT_HEADERS.merge("cookie" => []), [], []
- end
+ @status = 200
+ @header = DEFAULT_HEADERS.dup
+
+ @writer = lambda { |x| @body << x }
+ @block = nil
- def status; headers['Status'] end
- def status=(status) headers['Status'] = status end
+ @body = "",
+ @session, @assigns = [], []
+ end
def location; headers['Location'] end
def location=(url) headers['Location'] = url end
@@ -109,13 +110,17 @@ module ActionController # :nodoc:
def etag
headers['ETag']
end
-
+
def etag?
headers.include?('ETag')
end
-
+
def etag=(etag)
- headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}")
+ if etag.blank?
+ headers.delete('ETag')
+ else
+ headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}")
+ end
end
def redirect(url, status)
@@ -138,26 +143,77 @@ module ActionController # :nodoc:
handle_conditional_get!
set_content_length!
convert_content_type!
+ convert_language!
+ convert_expires!
+ convert_cookies!
+ end
+
+ def each(&callback)
+ if @body.respond_to?(:call)
+ @writer = lambda { |x| callback.call(x) }
+ @body.call(self, self)
+ elsif @body.is_a?(String)
+ @body.each_line(&callback)
+ else
+ @body.each(&callback)
+ end
+
+ @writer = callback
+ @block.call(self) if @block
+ end
+
+ def write(str)
+ @writer.call str.to_s
+ str
+ end
+
+ # Over Rack::Response#set_cookie to add HttpOnly option
+ def set_cookie(key, value)
+ case value
+ when Hash
+ domain = "; domain=" + value[:domain] if value[:domain]
+ path = "; path=" + value[:path] if value[:path]
+ # According to RFC 2109, we need dashes here.
+ # N.B.: cgi.rb uses spaces...
+ expires = "; expires=" + value[:expires].clone.gmtime.
+ strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
+ secure = "; secure" if value[:secure]
+ httponly = "; HttpOnly" if value[:http_only]
+ value = value[:value]
+ end
+ value = [value] unless Array === value
+ cookie = ::Rack::Utils.escape(key) + "=" +
+ value.map { |v| ::Rack::Utils.escape v }.join("&") +
+ "#{domain}#{path}#{expires}#{secure}#{httponly}"
+
+ case self["Set-Cookie"]
+ when Array
+ self["Set-Cookie"] << cookie
+ when String
+ self["Set-Cookie"] = [self["Set-Cookie"], cookie]
+ when nil
+ self["Set-Cookie"] = cookie
+ end
end
private
- def handle_conditional_get!
- if etag? || last_modified?
- set_conditional_cache_control!
- elsif nonempty_ok_response?
- self.etag = body
-
- if request && request.etag_matches?(etag)
- self.status = '304 Not Modified'
- self.body = ''
- end
-
- set_conditional_cache_control!
- end
+ def handle_conditional_get!
+ if etag? || last_modified?
+ set_conditional_cache_control!
+ elsif nonempty_ok_response?
+ self.etag = body
+
+ if request && request.etag_matches?(etag)
+ self.status = '304 Not Modified'
+ self.body = ''
+ end
+
+ set_conditional_cache_control!
+ end
end
def nonempty_ok_response?
- ok = !status || status[0..2] == '200'
+ ok = !status || status.to_s[0..2] == '200'
ok && body.is_a?(String) && !body.empty?
end
@@ -168,23 +224,32 @@ module ActionController # :nodoc:
end
def convert_content_type!
- if content_type = headers.delete("Content-Type")
- self.headers["type"] = content_type
- end
- if content_type = headers.delete("Content-type")
- self.headers["type"] = content_type
- end
- if content_type = headers.delete("content-type")
- self.headers["type"] = content_type
- end
+ headers['Content-Type'] ||= "text/html"
+ headers['Content-Type'] += "; charset=" + headers.delete('charset') if headers['charset']
end
-
- # Don't set the Content-Length for block-based bodies as that would mean reading it all into memory. Not nice
- # for, say, a 2GB streaming file.
+
+ # Don't set the Content-Length for block-based bodies as that would mean
+ # reading it all into memory. Not nice for, say, a 2GB streaming file.
def set_content_length!
- unless body.respond_to?(:call) || (status && status[0..2] == '304')
- self.headers["Content-Length"] ||= body.size
+ if status && status.to_s[0..2] == '204'
+ headers.delete('Content-Length')
+ elsif length = headers['Content-Length']
+ headers['Content-Length'] = length.to_s
+ elsif !body.respond_to?(:call) && (!status || status.to_s[0..2] != '304')
+ headers["Content-Length"] = body.size.to_s
end
end
+
+ def convert_language!
+ headers["Content-Language"] = headers.delete("language") if headers["language"]
+ end
+
+ def convert_expires!
+ headers["Expires"] = headers.delete("") if headers["expires"]
+ end
+
+ def convert_cookies!
+ headers['Set-Cookie'] = Array(headers['Set-Cookie']).compact
+ end
end
end
diff --git a/actionpack/lib/action_controller/routing/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb
index 3b98b16683..ebc553512f 100644
--- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb
+++ b/actionpack/lib/action_controller/routing/recognition_optimisation.rb
@@ -56,7 +56,7 @@ module ActionController
result = recognize_optimized(path, environment) and return result
# Route was not recognized. Try to find out why (maybe wrong verb).
- allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, :method => verb) } }
+ allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, environment.merge(:method => verb)) } }
if environment[:method] && !HTTP_METHODS.include?(environment[:method])
raise NotImplemented.new(*allows)
diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb
index 13646aef61..044ace7de1 100644
--- a/actionpack/lib/action_controller/routing/route_set.rb
+++ b/actionpack/lib/action_controller/routing/route_set.rb
@@ -145,10 +145,10 @@ module ActionController
def define_hash_access(route, name, kind, options)
selector = hash_access_name(name, kind)
named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks
- def #{selector}(options = nil)
- options ? #{options.inspect}.merge(options) : #{options.inspect}
- end
- protected :#{selector}
+ def #{selector}(options = nil) # def hash_for_users_url(options = nil)
+ options ? #{options.inspect}.merge(options) : #{options.inspect} # options ? {:only_path=>false}.merge(options) : {:only_path=>false}
+ end # end
+ protected :#{selector} # protected :hash_for_users_url
end_eval
helpers << selector
end
@@ -173,32 +173,33 @@ module ActionController
# foo_url(bar, baz, bang, :sort_by => 'baz')
#
named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks
- def #{selector}(*args)
-
- #{generate_optimisation_block(route, kind)}
-
- opts = if args.empty? || Hash === args.first
- args.first || {}
- else
- options = args.extract_options!
- args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)|
- h[k] = v
- h
- end
- options.merge(args)
- end
-
- url_for(#{hash_access_method}(opts))
-
- end
- #Add an alias to support the now deprecated formatted_* URL.
- def formatted_#{selector}(*args)
- ActiveSupport::Deprecation.warn(
- "formatted_#{selector}() has been deprecated. please pass format to the standard" +
- "#{selector}() method instead.", caller)
- #{selector}(*args)
- end
- protected :#{selector}
+ def #{selector}(*args) # def users_url(*args)
+ #
+ #{generate_optimisation_block(route, kind)} # #{generate_optimisation_block(route, kind)}
+ #
+ opts = if args.empty? || Hash === args.first # opts = if args.empty? || Hash === args.first
+ args.first || {} # args.first || {}
+ else # else
+ options = args.extract_options! # options = args.extract_options!
+ args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)| # args = args.zip([]).inject({}) do |h, (v, k)|
+ h[k] = v # h[k] = v
+ h # h
+ end # end
+ options.merge(args) # options.merge(args)
+ end # end
+ #
+ url_for(#{hash_access_method}(opts)) # url_for(hash_for_users_url(opts))
+ #
+ end # end
+ #Add an alias to support the now deprecated formatted_* URL. # #Add an alias to support the now deprecated formatted_* URL.
+ def formatted_#{selector}(*args) # def formatted_users_url(*args)
+ ActiveSupport::Deprecation.warn( # ActiveSupport::Deprecation.warn(
+ "formatted_#{selector}() has been deprecated. " + # "formatted_users_url() has been deprecated. " +
+ "Please pass format to the standard " + # "Please pass format to the standard " +
+ "#{selector} method instead.", caller) # "users_url method instead.", caller)
+ #{selector}(*args) # users_url(*args)
+ end # end
+ protected :#{selector} # protected :users_url
end_eval
helpers << selector
end
@@ -426,6 +427,12 @@ module ActionController
end
end
+ def call(env)
+ request = Request.new(env)
+ app = Routing::Routes.recognize(request)
+ app.call(env).to_a
+ end
+
def recognize(request)
params = recognize_path(request.path, extract_request_environment(request))
request.path_parameters = params.with_indifferent_access
diff --git a/actionpack/lib/action_controller/session/abstract_store.rb b/actionpack/lib/action_controller/session/abstract_store.rb
new file mode 100644
index 0000000000..bf09fd33c5
--- /dev/null
+++ b/actionpack/lib/action_controller/session/abstract_store.rb
@@ -0,0 +1,166 @@
+require 'rack/utils'
+
+module ActionController
+ module Session
+ class AbstractStore
+ ENV_SESSION_KEY = 'rack.session'.freeze
+ ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
+
+ HTTP_COOKIE = 'HTTP_COOKIE'.freeze
+ SET_COOKIE = 'Set-Cookie'.freeze
+
+ class SessionHash < Hash
+ def initialize(by, env)
+ super()
+ @by = by
+ @env = env
+ @loaded = false
+ end
+
+ def id
+ load! unless @loaded
+ @id
+ end
+
+ def session_id
+ ActiveSupport::Deprecation.warn(
+ "ActionController::Session::AbstractStore::SessionHash#session_id" +
+ "has been deprecated.Please use #id instead.", caller)
+ id
+ end
+
+ def [](key)
+ load! unless @loaded
+ super
+ end
+
+ def []=(key, value)
+ load! unless @loaded
+ super
+ end
+
+ def to_hash
+ h = {}.replace(self)
+ h.delete_if { |k,v| v.nil? }
+ h
+ end
+
+ def data
+ ActiveSupport::Deprecation.warn(
+ "ActionController::Session::AbstractStore::SessionHash#data" +
+ "has been deprecated.Please use #to_hash instead.", caller)
+ to_hash
+ end
+
+ private
+ def loaded?
+ @loaded
+ end
+
+ def load!
+ @id, session = @by.send(:load_session, @env)
+ replace(session)
+ @loaded = true
+ end
+ end
+
+ DEFAULT_OPTIONS = {
+ :key => '_session_id',
+ :path => '/',
+ :domain => nil,
+ :expire_after => nil,
+ :secure => false,
+ :httponly => true,
+ :cookie_only => true
+ }
+
+ def initialize(app, options = {})
+ # Process legacy CGI options
+ options = options.symbolize_keys
+ if options.has_key?(:session_path)
+ options[:path] = options.delete(:session_path)
+ end
+ if options.has_key?(:session_key)
+ options[:key] = options.delete(:session_key)
+ end
+ if options.has_key?(:session_http_only)
+ options[:httponly] = options.delete(:session_http_only)
+ end
+
+ @app = app
+ @default_options = DEFAULT_OPTIONS.merge(options)
+ @key = @default_options[:key]
+ @cookie_only = @default_options[:cookie_only]
+ end
+
+ def call(env)
+ session = SessionHash.new(self, env)
+
+ env[ENV_SESSION_KEY] = session
+ env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
+
+ response = @app.call(env)
+
+ session_data = env[ENV_SESSION_KEY]
+ if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?)
+ options = env[ENV_SESSION_OPTIONS_KEY]
+
+ if session_data.is_a?(AbstractStore::SessionHash)
+ sid = session_data.id
+ else
+ sid = generate_sid
+ end
+
+ unless set_session(env, sid, session_data.to_hash)
+ return response
+ end
+
+ cookie = Rack::Utils.escape(@key) + '=' + Rack::Utils.escape(sid)
+ cookie << "; domain=#{options[:domain]}" if options[:domain]
+ cookie << "; path=#{options[:path]}" if options[:path]
+ if options[:expire_after]
+ expiry = Time.now + options[:expire_after]
+ cookie << "; expires=#{expiry.httpdate}"
+ end
+ cookie << "; Secure" if options[:secure]
+ cookie << "; HttpOnly" if options[:httponly]
+
+ headers = response[1]
+ case a = headers[SET_COOKIE]
+ when Array
+ a << cookie
+ when String
+ headers[SET_COOKIE] = [a, cookie]
+ when nil
+ headers[SET_COOKIE] = cookie
+ end
+ end
+
+ response
+ end
+
+ private
+ def generate_sid
+ ActiveSupport::SecureRandom.hex(16)
+ end
+
+ def load_session(env)
+ request = Rack::Request.new(env)
+ sid = request.cookies[@key]
+ unless @cookie_only
+ sid ||= request.params[@key]
+ end
+ sid, session = get_session(env, sid)
+ [sid, session]
+ end
+
+ def get_session(env, sid)
+ raise '#get_session needs to be implemented.'
+ end
+
+ def set_session(env, sid, session_data)
+ raise '#set_session needs to be implemented.'
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/session/active_record_store.rb b/actionpack/lib/action_controller/session/active_record_store.rb
deleted file mode 100644
index fadf2a6b32..0000000000
--- a/actionpack/lib/action_controller/session/active_record_store.rb
+++ /dev/null
@@ -1,350 +0,0 @@
-require 'cgi'
-require 'cgi/session'
-require 'digest/md5'
-
-class CGI
- class Session
- attr_reader :data
-
- # Return this session's underlying Session instance. Useful for the DB-backed session stores.
- def model
- @dbman.model if @dbman
- end
-
-
- # 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+ (text, or longtext if your session data exceeds 65K), 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/environment.rb</tt>:
- # CGI::Session::ActiveRecordStore::Session.table_name = 'legacy_session_table'
- # CGI::Session::ActiveRecordStore::Session.primary_key = 'session_id'
- # CGI::Session::ActiveRecordStore::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
- # CGI::Session::ActiveRecordStore.session_class = MySessionClass
- # You must implement these methods:
- # self.find_by_session_id(session_id)
- # initialize(hash_of_session_id_and_data)
- # 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 ActiveRecordStore
- # The default Active Record class.
- class Session < ActiveRecord::Base
- ##
- # :singleton-method:
- # Customizable data column name. Defaults to 'data'.
- cattr_accessor :data_column_name
- self.data_column_name = 'data'
-
- before_save :marshal_data!
- before_save :raise_on_session_data_overflow!
-
- class << self
- # Don't try to reload ARStore::Session in dev mode.
- def reloadable? #:nodoc:
- false
- end
-
- 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
-
- def marshal(data) ActiveSupport::Base64.encode64(Marshal.dump(data)) if data end
- def unmarshal(data) Marshal.load(ActiveSupport::Base64.decode64(data)) if data end
-
- def create_table!
- connection.execute <<-end_sql
- CREATE TABLE #{table_name} (
- id INTEGER PRIMARY KEY,
- #{connection.quote_column_name('session_id')} TEXT UNIQUE,
- #{connection.quote_column_name(@@data_column_name)} TEXT(255)
- )
- end_sql
- end
-
- def drop_table!
- connection.execute "DROP TABLE #{table_name}"
- end
-
- private
- # 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
- def self.find_by_session_id(session_id)
- find :first, :conditions => ["session_id #{attribute_condition(session_id)}", session_id]
- end
- end
- end
- 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 if !loaded?
- write_attribute(@@data_column_name, self.class.marshal(self.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 if !loaded?
- limit = self.class.data_column_size_limit
- if loaded? and 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
- #
- # ActiveSupport::Base64.encode64(Marshal.dump(data))
- #
- # and unmarshaling data is
- #
- # Marshal.load(ActiveSupport::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
- ##
- # :singleton-method:
- # Use the ActiveRecord::Base.connection by default.
- cattr_accessor :connection
-
- ##
- # :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
-
- def connection
- @@connection ||= ActiveRecord::Base.connection
- 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 * FROM #{@@table_name} WHERE #{@@session_id_column}=#{@@connection.quote(session_id)}")
- new(:session_id => session_id, :marshaled_data => record['data'])
- end
- end
-
- def marshal(data) ActiveSupport::Base64.encode64(Marshal.dump(data)) if data end
- def unmarshal(data) Marshal.load(ActiveSupport::Base64.decode64(data)) if data end
-
- def create_table!
- @@connection.execute <<-end_sql
- CREATE TABLE #{table_name} (
- id INTEGER PRIMARY KEY,
- #{@@connection.quote_column_name(session_id_column)} TEXT UNIQUE,
- #{@@connection.quote_column_name(data_column)} TEXT
- )
- end_sql
- end
-
- def drop_table!
- @@connection.execute "DROP TABLE #{table_name}"
- end
- end
-
- attr_reader :session_id
- 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, @data, @marshaled_data = attributes[:session_id], attributes[:data], attributes[:marshaled_data]
- @new_record = @marshaled_data.nil?
- end
-
- def new_record?
- @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 if !loaded?
- marshaled_data = self.class.marshal(data)
-
- if @new_record
- @new_record = false
- @@connection.update <<-end_sql, 'Create session'
- INSERT INTO #{@@table_name} (
- #{@@connection.quote_column_name(@@session_id_column)},
- #{@@connection.quote_column_name(@@data_column)} )
- VALUES (
- #{@@connection.quote(session_id)},
- #{@@connection.quote(marshaled_data)} )
- end_sql
- else
- @@connection.update <<-end_sql, 'Update session'
- UPDATE #{@@table_name}
- SET #{@@connection.quote_column_name(@@data_column)}=#{@@connection.quote(marshaled_data)}
- WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)}
- end_sql
- end
- end
-
- def destroy
- unless @new_record
- @@connection.delete <<-end_sql, 'Destroy session'
- DELETE FROM #{@@table_name}
- WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)}
- end_sql
- end
- end
- end
-
-
- # The class used for session storage. Defaults to
- # CGI::Session::ActiveRecordStore::Session.
- cattr_accessor :session_class
- self.session_class = Session
-
- # Find or instantiate a session given a CGI::Session.
- def initialize(session, option = nil)
- session_id = session.session_id
- unless @session = ActiveRecord::Base.silence { @@session_class.find_by_session_id(session_id) }
- unless session.new_session
- raise CGI::Session::NoSession, 'uninitialized session'
- end
- @session = @@session_class.new(:session_id => session_id, :data => {})
- # session saving can be lazy again, because of improved component implementation
- # therefore next line gets commented out:
- # @session.save
- end
- end
-
- # Access the underlying session model.
- def model
- @session
- end
-
- # Restore session state. The session model handles unmarshaling.
- def restore
- if @session
- @session.data
- end
- end
-
- # Save session store.
- def update
- if @session
- ActiveRecord::Base.silence { @session.save }
- end
- end
-
- # Save and close the session store.
- def close
- if @session
- update
- @session = nil
- end
- end
-
- # Delete and close the session store.
- def delete
- if @session
- ActiveRecord::Base.silence { @session.destroy }
- @session = nil
- end
- end
-
- protected
- def logger
- ActionController::Base.logger rescue nil
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_controller/session/cookie_store.rb
index ea0ea4f841..135bedaf50 100644
--- a/actionpack/lib/action_controller/session/cookie_store.rb
+++ b/actionpack/lib/action_controller/session/cookie_store.rb
@@ -1,163 +1,224 @@
-require 'cgi'
-require 'cgi/session'
-
-# This cookie-based session store is the Rails default. Sessions typically
-# contain at most a user_id and flash message; both fit within the 4K cookie
-# size limit. Cookie-based sessions are dramatically faster than the
-# alternatives.
-#
-# If you have more than 4K of session data or don't want your data to be
-# visible to the user, pick another session store.
-#
-# CookieOverflow is raised if you attempt to store more than 4K of data.
-# TamperedWithCookie is raised if the data integrity check fails.
-#
-# A message digest is included with the cookie to ensure data integrity:
-# a user cannot alter his +user_id+ without knowing the secret key included in
-# the hash. New apps are generated with a pregenerated secret in
-# config/environment.rb. Set your own for old apps you're upgrading.
-#
-# Session options:
-#
-# * <tt>:secret</tt>: An application-wide key string or block returning a string
-# called per generated digest. The block is called with the CGI::Session
-# instance as an argument. It's important that the secret is not vulnerable to
-# a dictionary attack. Therefore, you should choose a secret consisting of
-# random numbers and letters and more than 30 characters. Examples:
-#
-# :secret => '449fe2e7daee471bffae2fd8dc02313d'
-# :secret => Proc.new { User.current_user.secret_key }
-#
-# * <tt>:digest</tt>: The message digest algorithm used to verify session
-# integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
-# such as 'MD5', 'RIPEMD160', 'SHA256', etc.
-#
-# To generate a secret key for an existing application, run
-# "rake secret" and set the key in config/environment.rb.
-#
-# Note that changing digest or secret invalidates all existing sessions!
-class CGI::Session::CookieStore
- # Cookies can typically store 4096 bytes.
- MAX = 4096
- SECRET_MIN_LENGTH = 30 # characters
-
- # Raised when storing more than 4K of session data.
- class CookieOverflow < StandardError; end
-
- # Raised when the cookie fails its integrity check.
- class TamperedWithCookie < StandardError; end
-
- # Called from CGI::Session only.
- def initialize(session, options = {})
- # The session_key option is required.
- if options['session_key'].blank?
- raise ArgumentError, 'A session_key is required to write a cookie containing the session data. Use config.action_controller.session = { :session_key => "_myapp_session", :secret => "some secret phrase" } in config/environment.rb'
- end
-
- # The secret option is required.
- ensure_secret_secure(options['secret'])
-
- # Keep the session and its secret on hand so we can read and write cookies.
- @session, @secret = session, options['secret']
-
- # Message digest defaults to SHA1.
- @digest = options['digest'] || 'SHA1'
-
- # Default cookie options derived from session settings.
- @cookie_options = {
- 'name' => options['session_key'],
- 'path' => options['session_path'],
- 'domain' => options['session_domain'],
- 'expires' => options['session_expires'],
- 'secure' => options['session_secure'],
- 'http_only' => options['session_http_only']
- }
-
- # Set no_hidden and no_cookies since the session id is unused and we
- # set our own data cookie.
- options['no_hidden'] = true
- options['no_cookies'] = true
- end
-
- # To prevent users from using something insecure like "Password" we make sure that the
- # secret they've provided is at least 30 characters in length.
- def ensure_secret_secure(secret)
- # There's no way we can do this check if they've provided a proc for the
- # secret.
- return true if secret.is_a?(Proc)
-
- if secret.blank?
- raise ArgumentError, %Q{A secret is required to generate an integrity hash for cookie session data. Use config.action_controller.session = { :session_key => "_myapp_session", :secret => "some secret phrase of at least #{SECRET_MIN_LENGTH} characters" } in config/environment.rb}
- end
-
- if secret.length < SECRET_MIN_LENGTH
- raise ArgumentError, %Q{Secret should be something secure, like "#{CGI::Session.generate_unique_id}". The value you provided, "#{secret}", is shorter than the minimum length of #{SECRET_MIN_LENGTH} characters}
- end
- end
-
- # Restore session data from the cookie.
- def restore
- @original = read_cookie
- @data = unmarshal(@original) || {}
- end
-
- # Wait until close to write the session data cookie.
- def update; end
-
- # Write the session data cookie if it was loaded and has changed.
- def close
- if defined?(@data) && !@data.blank?
- updated = marshal(@data)
- raise CookieOverflow if updated.size > MAX
- write_cookie('value' => updated) unless updated == @original
- end
- end
-
- # Delete the session data by setting an expired cookie with no data.
- def delete
- @data = nil
- clear_old_cookie_value
- write_cookie('value' => nil, 'expires' => 1.year.ago)
- end
-
- private
- # Marshal a session hash into safe cookie data. Include an integrity hash.
- def marshal(session)
- verifier.generate(session)
- end
-
- # Unmarshal cookie data to a hash and verify its integrity.
- def unmarshal(cookie)
- if cookie
- verifier.verify(cookie)
+module ActionController
+ module Session
+ # This cookie-based session store is the Rails default. Sessions typically
+ # contain at most a user_id and flash message; both fit within the 4K cookie
+ # size limit. Cookie-based sessions are dramatically faster than the
+ # alternatives.
+ #
+ # If you have more than 4K of session data or don't want your data to be
+ # visible to the user, pick another session store.
+ #
+ # CookieOverflow is raised if you attempt to store more than 4K of data.
+ #
+ # A message digest is included with the cookie to ensure data integrity:
+ # a user cannot alter his +user_id+ without knowing the secret key
+ # included in the hash. New apps are generated with a pregenerated secret
+ # in config/environment.rb. Set your own for old apps you're upgrading.
+ #
+ # Session options:
+ #
+ # * <tt>:secret</tt>: An application-wide key string or block returning a
+ # string called per generated digest. The block is called with the
+ # CGI::Session instance as an argument. It's important that the secret
+ # is not vulnerable to a dictionary attack. Therefore, you should choose
+ # a secret consisting of random numbers and letters and more than 30
+ # characters. Examples:
+ #
+ # :secret => '449fe2e7daee471bffae2fd8dc02313d'
+ # :secret => Proc.new { User.current_user.secret_key }
+ #
+ # * <tt>:digest</tt>: The message digest algorithm used to verify session
+ # integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
+ # such as 'MD5', 'RIPEMD160', 'SHA256', etc.
+ #
+ # To generate a secret key for an existing application, run
+ # "rake secret" and set the key in config/environment.rb.
+ #
+ # Note that changing digest or secret invalidates all existing sessions!
+ class CookieStore
+ # Cookies can typically store 4096 bytes.
+ MAX = 4096
+ SECRET_MIN_LENGTH = 30 # characters
+
+ DEFAULT_OPTIONS = {
+ :key => '_session_id',
+ :domain => nil,
+ :path => "/",
+ :expire_after => nil,
+ :httponly => false
+ }.freeze
+
+ ENV_SESSION_KEY = "rack.session".freeze
+ ENV_SESSION_OPTIONS_KEY = "rack.session.options".freeze
+ HTTP_SET_COOKIE = "Set-Cookie".freeze
+
+ # Raised when storing more than 4K of session data.
+ class CookieOverflow < StandardError; end
+
+ def initialize(app, options = {})
+ options = options.dup
+
+ # Process legacy CGI options
+ options = options.symbolize_keys
+ if options.has_key?(:session_path)
+ options[:path] = options.delete(:session_path)
+ end
+ if options.has_key?(:session_key)
+ options[:key] = options.delete(:session_key)
+ end
+ if options.has_key?(:session_http_only)
+ options[:httponly] = options.delete(:session_http_only)
+ end
+
+ @app = app
+
+ # The session_key option is required.
+ ensure_session_key(options[:key])
+ @key = options.delete(:key).freeze
+
+ # The secret option is required.
+ ensure_secret_secure(options[:secret])
+ @secret = options.delete(:secret).freeze
+
+ @digest = options.delete(:digest) || 'SHA1'
+ @verifier = verifier_for(@secret, @digest)
+
+ @default_options = DEFAULT_OPTIONS.merge(options).freeze
+
+ freeze
end
- rescue ActiveSupport::MessageVerifier::InvalidSignature
- delete
- raise TamperedWithCookie
- end
- # Read the session data cookie.
- def read_cookie
- @session.cgi.cookies[@cookie_options['name']].first
- end
-
- # CGI likes to make you hack.
- def write_cookie(options)
- cookie = CGI::Cookie.new(@cookie_options.merge(options))
- @session.cgi.send :instance_variable_set, '@output_cookies', [cookie]
- end
-
- # Clear cookie value so subsequent new_session doesn't reload old data.
- def clear_old_cookie_value
- @session.cgi.cookies[@cookie_options['name']].clear
- end
-
- def verifier
- if @secret.respond_to?(:call)
- key = @secret.call
- else
- key = @secret
+ def call(env)
+ env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env)
+ env[ENV_SESSION_OPTIONS_KEY] = @default_options
+
+ status, headers, body = @app.call(env)
+
+ session_data = env[ENV_SESSION_KEY]
+ if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?)
+ session_data = marshal(session_data.to_hash)
+
+ raise CookieOverflow if session_data.size > MAX
+
+ options = env[ENV_SESSION_OPTIONS_KEY]
+ cookie = Hash.new
+ cookie[:value] = session_data
+ unless options[:expire_after].nil?
+ cookie[:expires] = Time.now + options[:expire_after]
+ end
+
+ cookie = build_cookie(@key, cookie.merge(options))
+ case headers[HTTP_SET_COOKIE]
+ when Array
+ headers[HTTP_SET_COOKIE] << cookie
+ when String
+ headers[HTTP_SET_COOKIE] = [headers[HTTP_SET_COOKIE], cookie]
+ when nil
+ headers[HTTP_SET_COOKIE] = cookie
+ end
+ end
+
+ [status, headers, body]
end
- ActiveSupport::MessageVerifier.new(key, @digest)
+
+ private
+ # Should be in Rack::Utils soon
+ def build_cookie(key, value)
+ case value
+ when Hash
+ domain = "; domain=" + value[:domain] if value[:domain]
+ path = "; path=" + value[:path] if value[:path]
+ # According to RFC 2109, we need dashes here.
+ # N.B.: cgi.rb uses spaces...
+ expires = "; expires=" + value[:expires].clone.gmtime.
+ strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
+ secure = "; secure" if value[:secure]
+ httponly = "; httponly" if value[:httponly]
+ value = value[:value]
+ end
+ value = [value] unless Array === value
+ cookie = Rack::Utils.escape(key) + "=" +
+ value.map { |v| Rack::Utils.escape(v) }.join("&") +
+ "#{domain}#{path}#{expires}#{secure}#{httponly}"
+ end
+
+ def load_session(env)
+ request = Rack::Request.new(env)
+ session_data = request.cookies[@key]
+ data = unmarshal(session_data) || persistent_session_id!({})
+ [data[:session_id], data]
+ end
+
+ # Marshal a session hash into safe cookie data. Include an integrity hash.
+ def marshal(session)
+ @verifier.generate(persistent_session_id!(session))
+ end
+
+ # Unmarshal cookie data to a hash and verify its integrity.
+ def unmarshal(cookie)
+ persistent_session_id!(@verifier.verify(cookie)) if cookie
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
+ nil
+ end
+
+ def ensure_session_key(key)
+ if key.blank?
+ raise ArgumentError, 'A session_key is required to write a ' +
+ 'cookie containing the session data. Use ' +
+ 'config.action_controller.session = { :session_key => ' +
+ '"_myapp_session", :secret => "some secret phrase" } in ' +
+ 'config/environment.rb'
+ end
+ end
+
+ # To prevent users from using something insecure like "Password" we make sure that the
+ # secret they've provided is at least 30 characters in length.
+ def ensure_secret_secure(secret)
+ # There's no way we can do this check if they've provided a proc for the
+ # secret.
+ return true if secret.is_a?(Proc)
+
+ if secret.blank?
+ raise ArgumentError, "A secret is required to generate an " +
+ "integrity hash for cookie session data. Use " +
+ "config.action_controller.session = { :session_key => " +
+ "\"_myapp_session\", :secret => \"some secret phrase of at " +
+ "least #{SECRET_MIN_LENGTH} characters\" } " +
+ "in config/environment.rb"
+ end
+
+ if secret.length < SECRET_MIN_LENGTH
+ raise ArgumentError, "Secret should be something secure, " +
+ "like \"#{ActiveSupport::SecureRandom.hex(16)}\". The value you " +
+ "provided, \"#{secret}\", is shorter than the minimum length " +
+ "of #{SECRET_MIN_LENGTH} characters"
+ end
+ end
+
+ def verifier_for(secret, digest)
+ key = secret.respond_to?(:call) ? secret.call : secret
+ ActiveSupport::MessageVerifier.new(key, digest)
+ end
+
+ def generate_sid
+ ActiveSupport::SecureRandom.hex(16)
+ end
+
+ def persistent_session_id!(data)
+ (data ||= {}).merge!(inject_persistent_session_id(data))
+ end
+
+ def inject_persistent_session_id(data)
+ requires_session_id?(data) ? { :session_id => generate_sid } : {}
+ end
+
+ def requires_session_id?(data)
+ if data
+ data.respond_to?(:key?) && !data.key?(:session_id)
+ else
+ true
+ end
+ end
end
+ end
end
diff --git a/actionpack/lib/action_controller/session/drb_server.rb b/actionpack/lib/action_controller/session/drb_server.rb
deleted file mode 100755
index 2caa27f62a..0000000000
--- a/actionpack/lib/action_controller/session/drb_server.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env ruby
-
-# This is a really simple session storage daemon, basically just a hash,
-# which is enabled for DRb access.
-
-require 'drb'
-
-session_hash = Hash.new
-session_hash.instance_eval { @mutex = Mutex.new }
-
-class <<session_hash
- def []=(key, value)
- @mutex.synchronize do
- super(key, value)
- end
- end
-
- def [](key)
- @mutex.synchronize do
- super(key)
- end
- end
-
- def delete(key)
- @mutex.synchronize do
- super(key)
- end
- end
-end
-
-DRb.start_service('druby://127.0.0.1:9192', session_hash)
-DRb.thread.join
diff --git a/actionpack/lib/action_controller/session/drb_store.rb b/actionpack/lib/action_controller/session/drb_store.rb
deleted file mode 100644
index 4feb2636e7..0000000000
--- a/actionpack/lib/action_controller/session/drb_store.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'cgi'
-require 'cgi/session'
-require 'drb'
-
-class CGI #:nodoc:all
- class Session
- class DRbStore
- @@session_data = DRbObject.new(nil, 'druby://localhost:9192')
-
- def initialize(session, option=nil)
- @session_id = session.session_id
- end
-
- def restore
- @h = @@session_data[@session_id] || {}
- end
-
- def update
- @@session_data[@session_id] = @h
- end
-
- def close
- update
- end
-
- def delete
- @@session_data.delete(@session_id)
- end
-
- def data
- @@session_data[@session_id]
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/session/mem_cache_store.rb b/actionpack/lib/action_controller/session/mem_cache_store.rb
index 2f08af663d..f745715a97 100644
--- a/actionpack/lib/action_controller/session/mem_cache_store.rb
+++ b/actionpack/lib/action_controller/session/mem_cache_store.rb
@@ -1,95 +1,48 @@
-# cgi/session/memcached.rb - persistent storage of marshalled session data
-#
-# == Overview
-#
-# This file provides the CGI::Session::MemCache class, which builds
-# persistence of storage data on top of the MemCache library. See
-# cgi/session.rb for more details on session storage managers.
-#
-
begin
- require 'cgi/session'
require_library_or_gem 'memcache'
- class CGI
- class Session
- # MemCache-based session storage class.
- #
- # This builds upon the top-level MemCache class provided by the
- # library file memcache.rb. Session data is marshalled and stored
- # in a memcached cache.
- class MemCacheStore
- def check_id(id) #:nodoc:#
- /[^0-9a-zA-Z]+/ =~ id.to_s ? false : true
- end
+ module ActionController
+ module Session
+ class MemCacheStore < AbstractStore
+ def initialize(app, options = {})
+ # Support old :expires option
+ options[:expire_after] ||= options[:expires]
- # Create a new CGI::Session::MemCache instance
- #
- # This constructor is used internally by CGI::Session. The
- # user does not generally need to call it directly.
- #
- # +session+ is the session for which this instance is being
- # created. The session id must only contain alphanumeric
- # characters; automatically generated session ids observe
- # this requirement.
- #
- # +options+ is a hash of options for the initializer. The
- # following options are recognized:
- #
- # cache:: an instance of a MemCache client to use as the
- # session cache.
- #
- # expires:: an expiry time value to use for session entries in
- # the session cache. +expires+ is interpreted in seconds
- # relative to the current time if it’s less than 60*60*24*30
- # (30 days), or as an absolute Unix time (e.g., Time#to_i) if
- # greater. If +expires+ is +0+, or not passed on +options+,
- # the entry will never expire.
- #
- # This session's memcache entry will be created if it does
- # not exist, or retrieved if it does.
- def initialize(session, options = {})
- id = session.session_id
- unless check_id(id)
- raise ArgumentError, "session_id '%s' is invalid" % id
- end
- @cache = options['cache'] || MemCache.new('localhost')
- @expires = options['expires'] || 0
- @session_key = "session:#{id}"
- @session_data = {}
- # Add this key to the store if haven't done so yet
- unless @cache.get(@session_key)
- @cache.add(@session_key, @session_data, @expires)
- end
- end
+ super
- # Restore session state from the session's memcache entry.
- #
- # Returns the session state as a hash.
- def restore
- @session_data = @cache[@session_key] || {}
- end
+ @default_options = {
+ :namespace => 'rack:session',
+ :memcache_server => 'localhost:11211'
+ }.merge(@default_options)
- # Save session state to the session's memcache entry.
- def update
- @cache.set(@session_key, @session_data, @expires)
- end
-
- # Update and close the session's memcache entry.
- def close
- update
- end
+ @pool = options[:cache] || MemCache.new(@default_options[:memcache_server], @default_options)
+ unless @pool.servers.any? { |s| s.alive? }
+ raise "#{self} unable to find server during initialization."
+ end
+ @mutex = Mutex.new
- # Delete the session's memcache entry.
- def delete
- @cache.delete(@session_key)
- @session_data = {}
- end
-
- def data
- @session_data
+ super
end
+ private
+ def get_session(env, sid)
+ sid ||= generate_sid
+ begin
+ session = @pool.get(sid) || {}
+ rescue MemCache::MemCacheError, Errno::ECONNREFUSED
+ session = {}
+ end
+ [sid, session]
+ end
+
+ def set_session(env, sid, session_data)
+ options = env['rack.session.options']
+ expiry = options[:expire_after] || 0
+ @pool.set(sid, session_data, expiry)
+ return true
+ rescue MemCache::MemCacheError, Errno::ECONNREFUSED
+ return false
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/session_management.rb b/actionpack/lib/action_controller/session_management.rb
index 60a9aec39c..f06a0da75c 100644
--- a/actionpack/lib/action_controller/session_management.rb
+++ b/actionpack/lib/action_controller/session_management.rb
@@ -3,8 +3,6 @@ module ActionController #:nodoc:
def self.included(base)
base.class_eval do
extend ClassMethods
- alias_method_chain :process, :session_management_support
- alias_method_chain :process_cleanup, :session_management_support
end
end
@@ -12,144 +10,45 @@ module ActionController #:nodoc:
# Set the session store to be used for keeping the session data between requests.
# By default, sessions are stored in browser cookies (<tt>:cookie_store</tt>),
# but you can also specify one of the other included stores (<tt>:active_record_store</tt>,
- # <tt>:p_store</tt>, <tt>:drb_store</tt>, <tt>:mem_cache_store</tt>, or
- # <tt>:memory_store</tt>) or your own custom class.
+ # <tt>:mem_cache_store</tt>, or your own custom class.
def session_store=(store)
- ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager] =
- store.is_a?(Symbol) ? CGI::Session.const_get(store == :drb_store ? "DRbStore" : store.to_s.camelize) : store
+ if store == :active_record_store
+ self.session_store = ActiveRecord::SessionStore
+ else
+ @@session_store = store.is_a?(Symbol) ?
+ Session.const_get(store.to_s.camelize) :
+ store
+ end
end
# Returns the session store class currently used.
def session_store
- ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager]
+ if defined? @@session_store
+ @@session_store
+ else
+ Session::CookieStore
+ end
+ end
+
+ def session=(options = {})
+ self.session_store = nil if options.delete(:disabled)
+ session_options.merge!(options)
end
# Returns the hash used to configure the session. Example use:
#
# ActionController::Base.session_options[:session_secure] = true # session only available over HTTPS
def session_options
- ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
- end
-
- # Specify how sessions ought to be managed for a subset of the actions on
- # the controller. Like filters, you can specify <tt>:only</tt> and
- # <tt>:except</tt> clauses to restrict the subset, otherwise options
- # apply to all actions on this controller.
- #
- # The session options are inheritable, as well, so if you specify them in
- # a parent controller, they apply to controllers that extend the parent.
- #
- # Usage:
- #
- # # turn off session management for all actions.
- # session :off
- #
- # # turn off session management for all actions _except_ foo and bar.
- # session :off, :except => %w(foo bar)
- #
- # # turn off session management for only the foo and bar actions.
- # session :off, :only => %w(foo bar)
- #
- # # the session will only work over HTTPS, but only for the foo action
- # session :only => :foo, :session_secure => true
- #
- # # the session by default uses HttpOnly sessions for security reasons.
- # # this can be switched off.
- # session :only => :foo, :session_http_only => false
- #
- # # the session will only be disabled for 'foo', and only if it is
- # # requested as a web service
- # session :off, :only => :foo,
- # :if => Proc.new { |req| req.parameters[:ws] }
- #
- # # the session will be disabled for non html/ajax requests
- # session :off,
- # :if => Proc.new { |req| !(req.format.html? || req.format.js?) }
- #
- # # turn the session back on, useful when it was turned off in the
- # # application controller, and you need it on in another controller
- # session :on
- #
- # All session options described for ActionController::Base.process_cgi
- # are valid arguments.
- def session(*args)
- options = args.extract_options!
-
- options[:disabled] = false if args.delete(:on)
- options[:disabled] = true if !args.empty?
- options[:only] = [*options[:only]].map { |o| o.to_s } if options[:only]
- options[:except] = [*options[:except]].map { |o| o.to_s } if options[:except]
- if options[:only] && options[:except]
- raise ArgumentError, "only one of either :only or :except are allowed"
- end
-
- write_inheritable_array(:session_options, [options])
- end
-
- # So we can declare session options in the Rails initializer.
- alias_method :session=, :session
-
- def cached_session_options #:nodoc:
- @session_options ||= read_inheritable_attribute(:session_options) || []
+ @session_options ||= {}
end
- def session_options_for(request, action) #:nodoc:
- if (session_options = cached_session_options).empty?
- {}
- else
- options = {}
-
- action = action.to_s
- session_options.each do |opts|
- next if opts[:if] && !opts[:if].call(request)
- if opts[:only] && opts[:only].include?(action)
- options.merge!(opts)
- elsif opts[:except] && !opts[:except].include?(action)
- options.merge!(opts)
- elsif !opts[:only] && !opts[:except]
- options.merge!(opts)
- end
- end
-
- if options.empty? then options
- else
- options.delete :only
- options.delete :except
- options.delete :if
- options[:disabled] ? false : options
- end
- end
+ def session(*args)
+ ActiveSupport::Deprecation.warn(
+ "Disabling sessions for a single controller has been deprecated. " +
+ "Sessions are now lazy loaded. So if you don't access them, " +
+ "consider them off. You can still modify the session cookie " +
+ "options with request.session_options.", caller)
end
end
-
- def process_with_session_management_support(request, response, method = :perform_action, *arguments) #:nodoc:
- set_session_options(request)
- process_without_session_management_support(request, response, method, *arguments)
- end
-
- private
- def set_session_options(request)
- request.session_options = self.class.session_options_for(request, request.parameters["action"] || "index")
- end
-
- def process_cleanup_with_session_management_support
- clear_persistent_model_associations
- process_cleanup_without_session_management_support
- end
-
- # Clear cached associations in session data so they don't overflow
- # the database field. Only applies to ActiveRecordStore since there
- # is not a standard way to iterate over session data.
- def clear_persistent_model_associations #:doc:
- if defined?(@_session) && @_session.respond_to?(:data)
- session_data = @_session.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
- end
end
end
diff --git a/actionpack/lib/action_controller/streaming.rb b/actionpack/lib/action_controller/streaming.rb
index 333fb61b45..e1786913a7 100644
--- a/actionpack/lib/action_controller/streaming.rb
+++ b/actionpack/lib/action_controller/streaming.rb
@@ -24,7 +24,8 @@ module ActionController #:nodoc:
# Options:
# * <tt>:filename</tt> - suggests a filename for the browser to use.
# Defaults to <tt>File.basename(path)</tt>.
- # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
+ # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
+ # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
# * <tt>:length</tt> - used to manually override the length (in bytes) of the content that
# is going to be sent to the client. Defaults to <tt>File.size(path)</tt>.
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
@@ -107,7 +108,8 @@ module ActionController #:nodoc:
#
# Options:
# * <tt>:filename</tt> - suggests a filename for the browser to use.
- # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'.
+ # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
+ # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
# Valid values are 'inline' and 'attachment' (default).
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
@@ -143,9 +145,16 @@ module ActionController #:nodoc:
disposition <<= %(; filename="#{options[:filename]}") if options[:filename]
+ content_type = options[:type]
+ if content_type.is_a?(Symbol)
+ raise ArgumentError, "Unknown MIME type #{options[:type]}" unless Mime::EXTENSION_LOOKUP.has_key?(content_type.to_s)
+ content_type = Mime::Type.lookup_by_extension(content_type.to_s)
+ end
+ content_type = content_type.to_s.strip # fixes a problem with extra '\r' with some browsers
+
headers.update(
'Content-Length' => options[:length],
- 'Content-Type' => options[:type].to_s.strip, # fixes a problem with extra '\r' with some browsers
+ 'Content-Type' => content_type,
'Content-Disposition' => disposition,
'Content-Transfer-Encoding' => 'binary'
)
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 79a8e1364d..0b0d0c799b 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -1,4 +1,5 @@
require 'active_support/test_case'
+require 'action_controller/test_process'
module ActionController
# Superclass for ActionController functional tests. Functional tests allow you to
@@ -93,10 +94,7 @@ module ActionController
# and cookies, though. For sessions, you just do:
#
# @request.session[:key] = "value"
- #
- # For cookies, you need to manually create the cookie, like this:
- #
- # @request.cookies["key"] = CGI::Cookie.new("key", "value")
+ # @request.cookies["key"] = "value"
#
# == Testing named routes
#
@@ -105,6 +103,8 @@ module ActionController
#
# assert_redirected_to page_url(:title => 'foo')
class TestCase < ActiveSupport::TestCase
+ include TestProcess
+
module Assertions
%w(response selector tag dom routing model).each do |kind|
include ActionController::Assertions.const_get("#{kind.camelize}Assertions")
@@ -127,17 +127,18 @@ module ActionController
#
# The exception is stored in the exception accessor for further inspection.
module RaiseActionExceptions
- attr_accessor :exception
+ protected
+ attr_accessor :exception
- def rescue_action_without_handler(e)
- self.exception = e
-
- if request.remote_addr == "0.0.0.0"
- raise(e)
- else
- super(e)
+ def rescue_action_without_handler(e)
+ self.exception = e
+
+ if request.remote_addr == "0.0.0.0"
+ raise(e)
+ else
+ super(e)
+ end
end
- end
end
setup :setup_controller_request_and_response
diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb
index cd3914f011..8180d4ee93 100644
--- a/actionpack/lib/action_controller/test_process.rb
+++ b/actionpack/lib/action_controller/test_process.rb
@@ -1,46 +1,17 @@
-require 'action_controller/test_case'
-
module ActionController #:nodoc:
- class Base
- attr_reader :assigns
-
- # Process a test request called with a TestRequest object.
- def self.process_test(request)
- new.process_test(request)
- end
-
- def process_test(request) #:nodoc:
- process(request, TestResponse.new)
- end
-
- def process_with_test(*args)
- returning process_without_test(*args) do
- @assigns = {}
- (instance_variable_names - @@protected_instance_variables).each do |var|
- value = instance_variable_get(var)
- @assigns[var[1..-1]] = value
- response.template.assigns[var[1..-1]] = value if response
- end
- end
- end
-
- alias_method_chain :process, :test
- end
-
- class TestRequest < AbstractRequest #:nodoc:
+ class TestRequest < Request #:nodoc:
attr_accessor :cookies, :session_options
- attr_accessor :query_parameters, :request_parameters, :path, :session
- attr_accessor :host, :user_agent
+ attr_accessor :query_parameters, :path, :session
+ attr_accessor :host
- def initialize(query_parameters = nil, request_parameters = nil, session = nil)
- @query_parameters = query_parameters || {}
- @request_parameters = request_parameters || {}
- @session = session || TestSession.new
+ def initialize
+ super(Rack::MockRequest.env_for("/"))
- initialize_containers
- initialize_default_values
+ @query_parameters = {}
+ @session = TestSession.new
- super()
+ initialize_default_values
+ initialize_containers
end
def reset_session
@@ -55,7 +26,11 @@ module ActionController #:nodoc:
# Either the RAW_POST_DATA environment variable or the URL-encoded request
# parameters.
def raw_post
- env['RAW_POST_DATA'] ||= returning(url_encoded_request_parameters) { |b| b.force_encoding(Encoding::BINARY) if b.respond_to?(:force_encoding) }
+ @env['RAW_POST_DATA'] ||= begin
+ data = url_encoded_request_parameters
+ data.force_encoding(Encoding::BINARY) if data.respond_to?(:force_encoding)
+ data
+ end
end
def port=(number)
@@ -125,26 +100,30 @@ module ActionController #:nodoc:
path_parameters[key.to_s] = value
end
end
+ raw_post # populate env['RAW_POST_DATA']
@parameters = nil # reset TestRequest#parameters to use the new path_parameters
end
def recycle!
- self.request_parameters = {}
self.query_parameters = {}
self.path_parameters = {}
unmemoize_all
end
+ def user_agent=(user_agent)
+ @env['HTTP_USER_AGENT'] = user_agent
+ end
+
private
def initialize_containers
- @env, @cookies = {}, {}
+ @cookies = {}
end
def initialize_default_values
@host = "test.host"
@request_uri = "/"
- @user_agent = "Rails Testing"
- self.remote_addr = "0.0.0.0"
+ @env['HTTP_USER_AGENT'] = "Rails Testing"
+ @env['REMOTE_ADDR'] = "0.0.0.0"
@env["SERVER_PORT"] = 80
@env['REQUEST_METHOD'] = "GET"
end
@@ -166,7 +145,7 @@ module ActionController #:nodoc:
module TestResponseBehavior #:nodoc:
# The response code of the request
def response_code
- status[0,3].to_i rescue 0
+ status.to_s[0,3].to_i rescue 0
end
# Returns a String to ensure compatibility with Net::HTTPResponse
@@ -221,8 +200,8 @@ module ActionController #:nodoc:
# Returns the template of the file which was used to
# render this response (or nil)
- def rendered_template
- template.instance_variable_get(:@_first_render)
+ def rendered
+ template.instance_variable_get(:@_rendered)
end
# A shortcut to the flash. Returns an empty hash if no session flash exists.
@@ -232,7 +211,7 @@ module ActionController #:nodoc:
# Do we have a flash?
def has_flash?
- !session['flash'].empty?
+ !flash.empty?
end
# Do we have a flash that has contents?
@@ -260,11 +239,16 @@ module ActionController #:nodoc:
!template_objects[name].nil?
end
- # Returns the response cookies, converted to a Hash of (name => CGI::Cookie) pairs
+ # Returns the response cookies, converted to a Hash of (name => value) pairs
#
- # assert_equal ['AuthorOfNewPage'], r.cookies['author'].value
+ # assert_equal 'AuthorOfNewPage', r.cookies['author']
def cookies
- headers['cookie'].inject({}) { |hash, cookie| hash[cookie.name] = cookie; hash }
+ cookies = {}
+ Array(headers['Set-Cookie']).each do |cookie|
+ key, value = cookie.split(";").first.split("=")
+ cookies[key] = value
+ end
+ cookies
end
# Returns binary content (downloadable file), converted to a String
@@ -285,8 +269,8 @@ module ActionController #:nodoc:
# TestResponse, which represent the HTTP response results of the requested
# controller actions.
#
- # See AbstractResponse for more information on controller response objects.
- class TestResponse < AbstractResponse
+ # See Response for more information on controller response objects.
+ class TestResponse < Response
include TestResponseBehavior
def recycle!
@@ -372,20 +356,33 @@ module ActionController #:nodoc:
module TestProcess
def self.included(base)
- # execute the request simulating a specific HTTP method and set/volley the response
- # TODO: this should be un-DRY'ed for the sake of API documentation.
- %w( get post put delete head ).each do |method|
- base.class_eval <<-EOV, __FILE__, __LINE__
- def #{method}(action, parameters = nil, session = nil, flash = nil)
- @request.env['REQUEST_METHOD'] = "#{method.upcase}" if defined?(@request)
- process(action, parameters, session, flash)
- end
- EOV
+ # Executes a request simulating GET HTTP method and set/volley the response
+ def get(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "GET")
+ end
+
+ # Executes a request simulating POST HTTP method and set/volley the response
+ def post(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "POST")
+ end
+
+ # Executes a request simulating PUT HTTP method and set/volley the response
+ def put(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "PUT")
+ end
+
+ # Executes a request simulating DELETE HTTP method and set/volley the response
+ def delete(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "DELETE")
+ end
+
+ # Executes a request simulating HEAD HTTP method and set/volley the response
+ def head(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "HEAD")
end
end
- # execute the request and set/volley the response
- def process(action, parameters = nil, session = nil, flash = nil)
+ def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
# Sanity check for required instance variables so we can give an
# understandable error message.
%w(@controller @request @response).each do |iv_name|
@@ -398,7 +395,7 @@ module ActionController #:nodoc:
@response.recycle!
@html_document = nil
- @request.env['REQUEST_METHOD'] ||= "GET"
+ @request.env['REQUEST_METHOD'] = http_method
@request.action = action.to_s
@@ -408,12 +405,14 @@ module ActionController #:nodoc:
@request.session = ActionController::TestSession.new(session) unless session.nil?
@request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
build_request_uri(action, parameters)
- @controller.process(@request, @response)
+
+ Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest
+ @controller.process_with_test(@request, @response)
end
def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
@request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
- @request.env['HTTP_ACCEPT'] = 'text/javascript, text/html, application/xml, text/xml, */*'
+ @request.env['HTTP_ACCEPT'] = [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
returning __send__(request_method, action, parameters, session, flash) do
@request.env.delete 'HTTP_X_REQUESTED_WITH'
@request.env.delete 'HTTP_ACCEPT'
@@ -430,7 +429,7 @@ module ActionController #:nodoc:
end
def session
- @response.session
+ @request.session
end
def flash
@@ -520,12 +519,24 @@ module ActionController #:nodoc:
ActionController::Routing.const_set(:Routes, real_routes) if real_routes
end
end
-end
-module Test
- module Unit
- class TestCase #:nodoc:
- include ActionController::TestProcess
+ module ProcessWithTest #:nodoc:
+ def self.included(base)
+ base.class_eval { attr_reader :assigns }
+ end
+
+ def process_with_test(*args)
+ process(*args).tap { set_test_assigns }
end
+
+ private
+ def set_test_assigns
+ @assigns = {}
+ (instance_variable_names - self.class.protected_instance_variables).each do |var|
+ name, value = var[1..-1], instance_variable_get(var)
+ @assigns[name] = value
+ response.template.assigns[name] = value if response
+ end
+ end
end
end
diff --git a/actionpack/lib/action_controller/uploaded_file.rb b/actionpack/lib/action_controller/uploaded_file.rb
new file mode 100644
index 0000000000..ea4845c68f
--- /dev/null
+++ b/actionpack/lib/action_controller/uploaded_file.rb
@@ -0,0 +1,37 @@
+module ActionController
+ module UploadedFile
+ def self.included(base)
+ base.class_eval do
+ attr_accessor :original_path, :content_type
+ alias_method :local_path, :path
+ end
+ end
+
+ # Take the basename of the upload's original filename.
+ # This handles the full Windows paths given by Internet Explorer
+ # (and perhaps other broken user agents) without affecting
+ # those which give the lone filename.
+ # The Windows regexp is adapted from Perl's File::Basename.
+ def original_filename
+ unless defined? @original_filename
+ @original_filename =
+ unless original_path.blank?
+ if original_path =~ /^(?:.*[:\\\/])?(.*)/m
+ $1
+ else
+ File.basename original_path
+ end
+ end
+ end
+ @original_filename
+ end
+ end
+
+ class UploadedStringIO < StringIO
+ include UploadedFile
+ end
+
+ class UploadedTempfile < Tempfile
+ include UploadedFile
+ end
+end
diff --git a/actionpack/lib/action_controller/url_encoded_pair_parser.rb b/actionpack/lib/action_controller/url_encoded_pair_parser.rb
new file mode 100644
index 0000000000..9883ad0d85
--- /dev/null
+++ b/actionpack/lib/action_controller/url_encoded_pair_parser.rb
@@ -0,0 +1,94 @@
+module ActionController
+ class UrlEncodedPairParser < StringScanner #:nodoc:
+ attr_reader :top, :parent, :result
+
+ def initialize(pairs = [])
+ super('')
+ @result = {}
+ pairs.each { |key, value| parse(key, value) }
+ end
+
+ KEY_REGEXP = %r{([^\[\]=&]+)}
+ BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]}
+
+ # Parse the query string
+ def parse(key, value)
+ self.string = key
+ @top, @parent = result, nil
+
+ # First scan the bare key
+ key = scan(KEY_REGEXP) or return
+ key = post_key_check(key)
+
+ # Then scan as many nestings as present
+ until eos?
+ r = scan(BRACKETED_KEY_REGEXP) or return
+ key = self[1]
+ key = post_key_check(key)
+ end
+
+ bind(key, value)
+ end
+
+ private
+ # After we see a key, we must look ahead to determine our next action. Cases:
+ #
+ # [] follows the key. Then the value must be an array.
+ # = follows the key. (A value comes next)
+ # & or the end of string follows the key. Then the key is a flag.
+ # otherwise, a hash follows the key.
+ def post_key_check(key)
+ if scan(/\[\]/) # a[b][] indicates that b is an array
+ container(key, Array)
+ nil
+ elsif check(/\[[^\]]/) # a[b] indicates that a is a hash
+ container(key, Hash)
+ nil
+ else # End of key? We do nothing.
+ key
+ end
+ end
+
+ # Add a container to the stack.
+ def container(key, klass)
+ type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass)
+ value = bind(key, klass.new)
+ type_conflict! klass, value unless value.is_a?(klass)
+ push(value)
+ end
+
+ # Push a value onto the 'stack', which is actually only the top 2 items.
+ def push(value)
+ @parent, @top = @top, value
+ end
+
+ # Bind a key (which may be nil for items in an array) to the provided value.
+ def bind(key, value)
+ if top.is_a? Array
+ if key
+ if top[-1].is_a?(Hash) && ! top[-1].key?(key)
+ top[-1][key] = value
+ else
+ top << {key => value}.with_indifferent_access
+ end
+ push top.last
+ return top[key]
+ else
+ top << value
+ return value
+ end
+ elsif top.is_a? Hash
+ key = CGI.unescape(key)
+ parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
+ top[key] ||= value
+ return top[key]
+ else
+ raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
+ end
+ end
+
+ def type_conflict!(klass, value)
+ raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)"
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/verb_piggybacking.rb b/actionpack/lib/action_controller/verb_piggybacking.rb
new file mode 100644
index 0000000000..86cde304a0
--- /dev/null
+++ b/actionpack/lib/action_controller/verb_piggybacking.rb
@@ -0,0 +1,24 @@
+module ActionController
+ # TODO: Use Rack::MethodOverride when it is released
+ class VerbPiggybacking
+ HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS)
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ if env["REQUEST_METHOD"] == "POST"
+ req = Request.new(env)
+ if method = (req.parameters[:_method] || env["HTTP_X_HTTP_METHOD_OVERRIDE"])
+ method = method.to_s.upcase
+ if HTTP_METHODS.include?(method)
+ env["REQUEST_METHOD"] = method
+ end
+ end
+ end
+
+ @app.call(env)
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 33517ffb7b..70a0ba91a7 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -3,7 +3,10 @@ module ActionView #:nodoc:
end
class MissingTemplate < ActionViewError #:nodoc:
+ attr_reader :path
+
def initialize(paths, path, template_format = nil)
+ @path = path
full_template_path = path.include?('.') ? path : "#{path}.erb"
display_paths = paths.compact.join(":")
template_type = (path =~ /layouts/i) ? 'layout' : 'template'
@@ -172,17 +175,6 @@ module ActionView #:nodoc:
delegate :logger, :to => 'ActionController::Base'
end
- # Templates that are exempt from layouts
- @@exempt_from_layout = Set.new([/\.rjs$/])
-
- # Don't render layouts for templates with the given extensions.
- def self.exempt_from_layout(*extensions)
- regexps = extensions.collect do |extension|
- extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
- end
- @@exempt_from_layout.merge(regexps)
- end
-
@@debug_rjs = false
##
# :singleton-method:
@@ -190,12 +182,6 @@ module ActionView #:nodoc:
# that alert()s the caught exception (and then re-raises it).
cattr_accessor :debug_rjs
- @@warn_cache_misses = false
- ##
- # :singleton-method:
- # A warning will be displayed whenever an action results in a cache miss on your view paths.
- cattr_accessor :warn_cache_misses
-
attr_internal :request
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
@@ -239,7 +225,7 @@ module ActionView #:nodoc:
end
# Returns the result of a render that's dictated by the options hash. The primary options are:
- #
+ #
# * <tt>:partial</tt> - See ActionView::Partials.
# * <tt>:update</tt> - Calls update_page with the block given.
# * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
@@ -257,7 +243,8 @@ module ActionView #:nodoc:
if options[:layout]
_render_with_layout(options, local_assigns, &block)
elsif options[:file]
- _pick_template(options[:file]).render_template(self, options[:locals])
+ tempalte = self.view_paths.find_template(options[:file], template_format)
+ tempalte.render_template(self, options[:locals])
elsif options[:partial]
render_partial(options)
elsif options[:inline]
@@ -315,45 +302,6 @@ module ActionView #:nodoc:
end
end
- def _pick_template(template_path)
- return template_path if template_path.respond_to?(:render)
-
- path = template_path.sub(/^\//, '')
- if m = path.match(/(.*)\.(\w+)$/)
- template_file_name, template_file_extension = m[1], m[2]
- else
- template_file_name = path
- end
-
- # OPTIMIZE: Checks to lookup template in view path
- if template = self.view_paths.find_template(template_file_name, template_format)
- template
- elsif (first_render = @_render_stack.first) && first_render.respond_to?(:format_and_extension) &&
- (template = self.view_paths["#{template_file_name}.#{first_render.format_and_extension}"])
- template
- else
- template = Template.new(template_path, view_paths)
-
- if self.class.warn_cache_misses && logger
- logger.debug "[PERFORMANCE] Rendering a template that was " +
- "not found in view path. Templates outside the view path are " +
- "not cached and result in expensive disk operations. Move this " +
- "file into #{view_paths.join(':')} or add the folder to your " +
- "view path list"
- end
-
- template
- end
- end
- memoize :_pick_template
-
- def _exempt_from_layout?(template_path) #:nodoc:
- template = _pick_template(template_path).to_s
- @@exempt_from_layout.any? { |ext| template =~ ext }
- rescue ActionView::MissingTemplate
- return false
- end
-
def _render_with_layout(options, local_assigns, &block) #:nodoc:
partial_layout = options.delete(:layout)
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 0633d5414e..58f8cca6be 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -157,7 +157,7 @@ module ActionView
# javascript_path "http://www.railsapplication.com/js/xmlhr" # => http://www.railsapplication.com/js/xmlhr.js
# javascript_path "http://www.railsapplication.com/js/xmlhr.js" # => http://www.railsapplication.com/js/xmlhr.js
def javascript_path(source)
- JavaScriptTag.new(self, @controller, source).public_path
+ compute_public_path(source, 'javascripts', 'js')
end
alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
@@ -255,17 +255,15 @@ module ActionView
joined_javascript_name = (cache == true ? "all" : cache) + ".js"
joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name)
- unless File.exists?(joined_javascript_path)
- JavaScriptSources.create(self, @controller, sources, recursive).write_asset_file_contents(joined_javascript_path)
- end
+ write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) unless File.exists?(joined_javascript_path)
javascript_src_tag(joined_javascript_name, options)
else
- JavaScriptSources.create(self, @controller, sources, recursive).expand_sources.collect { |source|
- javascript_src_tag(source, options)
- }.join("\n")
+ expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n")
end
end
+ @@javascript_expansions = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup }
+
# Register one or more javascript files to be included when <tt>symbol</tt>
# is passed to <tt>javascript_include_tag</tt>. This method is typically intended
# to be called from plugin initialization to register javascript files
@@ -278,9 +276,11 @@ module ActionView
# <script type="text/javascript" src="/javascripts/body.js"></script>
# <script type="text/javascript" src="/javascripts/tail.js"></script>
def self.register_javascript_expansion(expansions)
- JavaScriptSources.expansions.merge!(expansions)
+ @@javascript_expansions.merge!(expansions)
end
+ @@stylesheet_expansions = {}
+
# Register one or more stylesheet files to be included when <tt>symbol</tt>
# is passed to <tt>stylesheet_link_tag</tt>. This method is typically intended
# to be called from plugin initialization to register stylesheet files
@@ -293,7 +293,7 @@ module ActionView
# <link href="/stylesheets/body.css" media="screen" rel="stylesheet" type="text/css" />
# <link href="/stylesheets/tail.css" media="screen" rel="stylesheet" type="text/css" />
def self.register_stylesheet_expansion(expansions)
- StylesheetSources.expansions.merge!(expansions)
+ @@stylesheet_expansions.merge!(expansions)
end
# Register one or more additional JavaScript files to be included when
@@ -301,11 +301,11 @@ module ActionView
# typically intended to be called from plugin initialization to register additional
# .js files that the plugin installed in <tt>public/javascripts</tt>.
def self.register_javascript_include_default(*sources)
- JavaScriptSources.expansions[:defaults].concat(sources)
+ @@javascript_expansions[:defaults].concat(sources)
end
def self.reset_javascript_include_default #:nodoc:
- JavaScriptSources.expansions[:defaults] = JAVASCRIPT_DEFAULT_SOURCES.dup
+ @@javascript_expansions[:defaults] = JAVASCRIPT_DEFAULT_SOURCES.dup
end
# Computes the path to a stylesheet asset in the public stylesheets directory.
@@ -320,7 +320,7 @@ module ActionView
# stylesheet_path "http://www.railsapplication.com/css/style" # => http://www.railsapplication.com/css/style.css
# stylesheet_path "http://www.railsapplication.com/css/style.js" # => http://www.railsapplication.com/css/style.css
def stylesheet_path(source)
- StylesheetTag.new(self, @controller, source).public_path
+ compute_public_path(source, 'stylesheets', 'css')
end
alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
@@ -365,7 +365,6 @@ module ActionView
# compressed by gzip (leading to faster transfers). Caching will only happen if ActionController::Base.perform_caching
# is set to true (which is the case by default for the Rails production environment, but not for the development
# environment). Examples:
-
#
# ==== Examples
# stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is false =>
@@ -396,14 +395,10 @@ module ActionView
joined_stylesheet_name = (cache == true ? "all" : cache) + ".css"
joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name)
- unless File.exists?(joined_stylesheet_path)
- StylesheetSources.create(self, @controller, sources, recursive).write_asset_file_contents(joined_stylesheet_path)
- end
+ write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) unless File.exists?(joined_stylesheet_path)
stylesheet_tag(joined_stylesheet_name, options)
else
- StylesheetSources.create(self, @controller, sources, recursive).expand_sources.collect { |source|
- stylesheet_tag(source, options)
- }.join("\n")
+ expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n")
end
end
@@ -418,7 +413,7 @@ module ActionView
# image_path("/icons/edit.png") # => /icons/edit.png
# image_path("http://www.railsapplication.com/img/edit.png") # => http://www.railsapplication.com/img/edit.png
def image_path(source)
- ImageTag.new(self, @controller, source).public_path
+ compute_public_path(source, 'images')
end
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
@@ -473,353 +468,190 @@ module ActionView
tag("img", options)
end
- private
- def javascript_src_tag(source, options)
- content_tag("script", "", { "type" => Mime::JS, "src" => path_to_javascript(source) }.merge(options))
- end
-
- def stylesheet_tag(source, options)
- tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => html_escape(path_to_stylesheet(source)) }.merge(options), false, false)
- end
-
- module ImageAsset
- DIRECTORY = 'images'.freeze
-
- def directory
- DIRECTORY
- end
-
- def extension
- nil
- end
- end
-
- module JavaScriptAsset
- DIRECTORY = 'javascripts'.freeze
- EXTENSION = 'js'.freeze
-
- def public_directory
- JAVASCRIPTS_DIR
- end
-
- def directory
- DIRECTORY
- end
+ def self.cache_asset_timestamps
+ @@cache_asset_timestamps
+ end
- def extension
- EXTENSION
- end
- end
+ # You can enable or disable the asset tag timestamps cache.
+ # With the cache enabled, the asset tag helper methods will make fewer
+ # expense file system calls. However this prevents you from modifying
+ # any asset files while the server is running.
+ #
+ # ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
+ def self.cache_asset_timestamps=(value)
+ @@cache_asset_timestamps = value
+ end
- module StylesheetAsset
- DIRECTORY = 'stylesheets'.freeze
- EXTENSION = 'css'.freeze
+ @@cache_asset_timestamps = true
- def public_directory
- STYLESHEETS_DIR
+ private
+ # Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
+ # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
+ # roots. Rewrite the asset path for cache-busting asset ids. Include
+ # asset host, if configured, with the correct request protocol.
+ def compute_public_path(source, dir, ext = nil, include_host = true)
+ has_request = @controller.respond_to?(:request)
+
+ if ext && (File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}")))
+ source += ".#{ext}"
end
- def directory
- DIRECTORY
- end
+ unless source =~ %r{^[-a-z]+://}
+ source = "/#{dir}/#{source}" unless source[0] == ?/
- def extension
- EXTENSION
- end
- end
+ source = rewrite_asset_path(source)
- class AssetTag
- extend ActiveSupport::Memoizable
-
- Cache = {}
- CacheGuard = Mutex.new
-
- ProtocolRegexp = %r{^[-a-z]+://}.freeze
-
- def initialize(template, controller, source, include_host = true)
- # NOTE: The template arg is temporarily needed for a legacy plugin
- # hook that is expected to call rewrite_asset_path on the
- # template. This should eventually be removed.
- @template = template
- @controller = controller
- @source = source
- @include_host = include_host
- @cache_key = if controller.respond_to?(:request)
- [self.class.name,controller.request.protocol,
- ActionController::Base.asset_host,
- ActionController::Base.relative_url_root,
- source, include_host]
- else
- [self.class.name,ActionController::Base.asset_host, source, include_host]
+ if has_request && include_host
+ unless source =~ %r{^#{ActionController::Base.relative_url_root}/}
+ source = "#{ActionController::Base.relative_url_root}#{source}"
+ end
end
end
-
- def public_path
- compute_public_path(@source)
- end
- memoize :public_path
- def asset_file_path
- File.join(ASSETS_DIR, public_path.split('?').first)
- end
- memoize :asset_file_path
-
- def contents
- File.read(asset_file_path)
- end
-
- def mtime
- File.mtime(asset_file_path)
- end
-
- private
- def request
- request? && @controller.request
- end
+ if include_host && source !~ %r{^[-a-z]+://}
+ host = compute_asset_host(source)
- def request?
- @controller.respond_to?(:request)
+ if has_request && !host.blank? && host !~ %r{^[-a-z]+://}
+ host = "#{@controller.request.protocol}#{host}"
end
- # Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
- # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
- # roots. Rewrite the asset path for cache-busting asset ids. Include
- # asset host, if configured, with the correct request protocol.
- def compute_public_path(source)
- if source =~ ProtocolRegexp
- source += ".#{extension}" if missing_extension?(source)
- source = prepend_asset_host(source)
- source
- else
- CacheGuard.synchronize do
- Cache[@cache_key + [source]] ||= begin
- source += ".#{extension}" if missing_extension?(source) || file_exists_with_extension?(source)
- source = "/#{directory}/#{source}" unless source[0] == ?/
- source = rewrite_asset_path(source)
- source = prepend_relative_url_root(source)
- source = prepend_asset_host(source)
- source
- end
- end
- end
- end
-
- def missing_extension?(source)
- extension && File.extname(source).blank?
- end
-
- def file_exists_with_extension?(source)
- extension && File.exist?(File.join(ASSETS_DIR, directory, "#{source}.#{extension}"))
- end
-
- def prepend_relative_url_root(source)
- relative_url_root = ActionController::Base.relative_url_root
- if request? && @include_host && source !~ %r{^#{relative_url_root}/}
- "#{relative_url_root}#{source}"
- else
- source
- end
- end
+ "#{host}#{source}"
+ else
+ source
+ end
+ end
- def prepend_asset_host(source)
- if @include_host && source !~ ProtocolRegexp
- host = compute_asset_host(source)
- if request? && !host.blank? && host !~ ProtocolRegexp
- host = "#{request.protocol}#{host}"
- end
- "#{host}#{source}"
+ # Pick an asset host for this source. Returns +nil+ if no host is set,
+ # the host if no wildcard is set, the host interpolated with the
+ # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
+ # or the value returned from invoking the proc if it's a proc or the value from
+ # invoking call if it's an object responding to call.
+ def compute_asset_host(source)
+ if host = ActionController::Base.asset_host
+ if host.is_a?(Proc) || host.respond_to?(:call)
+ case host.is_a?(Proc) ? host.arity : host.method(:call).arity
+ when 2
+ request = @controller.respond_to?(:request) && @controller.request
+ host.call(source, request)
else
- source
- end
- end
-
- # Pick an asset host for this source. Returns +nil+ if no host is set,
- # the host if no wildcard is set, the host interpolated with the
- # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
- # or the value returned from invoking the proc if it's a proc or the value from
- # invoking call if it's an object responding to call.
- def compute_asset_host(source)
- if host = ActionController::Base.asset_host
- if host.is_a?(Proc) || host.respond_to?(:call)
- case host.is_a?(Proc) ? host.arity : host.method(:call).arity
- when 2
- host.call(source, request)
- else
- host.call(source)
- end
- else
- (host =~ /%d/) ? host % (source.hash % 4) : host
- end
+ host.call(source)
end
+ else
+ (host =~ /%d/) ? host % (source.hash % 4) : host
end
+ end
+ end
- # Use the RAILS_ASSET_ID environment variable or the source's
- # modification time as its cache-busting asset id.
- def rails_asset_id(source)
- if asset_id = ENV["RAILS_ASSET_ID"]
- asset_id
- else
- path = File.join(ASSETS_DIR, source)
+ @@asset_timestamps_cache = {}
+ @@asset_timestamps_cache_guard = Mutex.new
+
+ # Use the RAILS_ASSET_ID environment variable or the source's
+ # modification time as its cache-busting asset id.
+ def rails_asset_id(source)
+ if asset_id = ENV["RAILS_ASSET_ID"]
+ asset_id
+ else
+ if @@cache_asset_timestamps && (asset_id = @@asset_timestamps_cache[source])
+ asset_id
+ else
+ path = File.join(ASSETS_DIR, source)
+ asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : ''
- if File.exist?(path)
- File.mtime(path).to_i.to_s
- else
- ''
+ if @@cache_asset_timestamps
+ @@asset_timestamps_cache_guard.synchronize do
+ @@asset_timestamps_cache[source] = asset_id
end
end
- end
- # Break out the asset path rewrite in case plugins wish to put the asset id
- # someplace other than the query string.
- def rewrite_asset_path(source)
- if @template.respond_to?(:rewrite_asset_path)
- # DEPRECATE: This way to override rewrite_asset_path
- @template.send(:rewrite_asset_path, source)
- else
- asset_id = rails_asset_id(source)
- if asset_id.blank?
- source
- else
- "#{source}?#{asset_id}"
- end
- end
+ asset_id
end
+ end
end
- class ImageTag < AssetTag
- include ImageAsset
+ # Break out the asset path rewrite in case plugins wish to put the asset id
+ # someplace other than the query string.
+ def rewrite_asset_path(source)
+ asset_id = rails_asset_id(source)
+ if asset_id.blank?
+ source
+ else
+ source + "?#{asset_id}"
+ end
end
- class JavaScriptTag < AssetTag
- include JavaScriptAsset
+ def javascript_src_tag(source, options)
+ content_tag("script", "", { "type" => Mime::JS, "src" => path_to_javascript(source) }.merge(options))
end
- class StylesheetTag < AssetTag
- include StylesheetAsset
+ def stylesheet_tag(source, options)
+ tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => html_escape(path_to_stylesheet(source)) }.merge(options), false, false)
end
- class AssetCollection
- extend ActiveSupport::Memoizable
-
- Cache = {}
- CacheGuard = Mutex.new
-
- def self.create(template, controller, sources, recursive)
- CacheGuard.synchronize do
- key = [self, sources, recursive]
- Cache[key] ||= new(template, controller, sources, recursive).freeze
- end
- end
+ def compute_javascript_paths(*args)
+ expand_javascript_sources(*args).collect { |source| compute_public_path(source, 'javascripts', 'js', false) }
+ end
- def initialize(template, controller, sources, recursive)
- # NOTE: The template arg is temporarily needed for a legacy plugin
- # hook. See NOTE under AssetTag#initialize for more details
- @template = template
- @controller = controller
- @sources = sources
- @recursive = recursive
- end
+ def compute_stylesheet_paths(*args)
+ expand_stylesheet_sources(*args).collect { |source| compute_public_path(source, 'stylesheets', 'css', false) }
+ end
- def write_asset_file_contents(joined_asset_path)
- FileUtils.mkdir_p(File.dirname(joined_asset_path))
- File.open(joined_asset_path, "w+") { |cache| cache.write(joined_contents) }
- mt = latest_mtime
- File.utime(mt, mt, joined_asset_path)
+ def expand_javascript_sources(sources, recursive = false)
+ if sources.include?(:all)
+ all_javascript_files = collect_asset_files(JAVASCRIPTS_DIR, ('**' if recursive), '*.js')
+ ((determine_source(:defaults, @@javascript_expansions).dup & all_javascript_files) + all_javascript_files).uniq
+ else
+ expanded_sources = sources.collect do |source|
+ determine_source(source, @@javascript_expansions)
+ end.flatten
+ expanded_sources << "application" if sources.include?(:defaults) && File.exist?(File.join(JAVASCRIPTS_DIR, "application.js"))
+ expanded_sources
end
-
- private
- def determine_source(source, collection)
- case source
- when Symbol
- collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
- else
- source
- end
- end
-
- def validate_sources!
- @sources.collect { |source| determine_source(source, self.class.expansions) }.flatten
- end
-
- def all_asset_files
- path = [public_directory, ('**' if @recursive), "*.#{extension}"].compact
- Dir[File.join(*path)].collect { |file|
- file[-(file.size - public_directory.size - 1)..-1].sub(/\.\w+$/, '')
- }.sort
- end
-
- def tag_sources
- expand_sources.collect { |source| tag_class.new(@template, @controller, source, false) }
- end
-
- def joined_contents
- tag_sources.collect { |source| source.contents }.join("\n\n")
- end
-
- # Set mtime to the latest of the combined files to allow for
- # consistent ETag without a shared filesystem.
- def latest_mtime
- tag_sources.map { |source| source.mtime }.max
- end
end
- class JavaScriptSources < AssetCollection
- include JavaScriptAsset
-
- EXPANSIONS = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup }
-
- def self.expansions
- EXPANSIONS
+ def expand_stylesheet_sources(sources, recursive)
+ if sources.first == :all
+ collect_asset_files(STYLESHEETS_DIR, ('**' if recursive), '*.css')
+ else
+ sources.collect do |source|
+ determine_source(source, @@stylesheet_expansions)
+ end.flatten
end
+ end
- APPLICATION_JS = "application".freeze
- APPLICATION_FILE = "application.js".freeze
-
- def expand_sources
- if @sources.include?(:all)
- assets = all_asset_files
- ((defaults.dup & assets) + assets).uniq!
- else
- expanded_sources = validate_sources!
- expanded_sources << APPLICATION_JS if include_application?
- expanded_sources
- end
+ def determine_source(source, collection)
+ case source
+ when Symbol
+ collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
+ else
+ source
end
- memoize :expand_sources
-
- private
- def tag_class
- JavaScriptTag
- end
-
- def defaults
- determine_source(:defaults, self.class.expansions)
- end
+ end
- def include_application?
- @sources.include?(:defaults) && File.exist?(File.join(JAVASCRIPTS_DIR, APPLICATION_FILE))
- end
+ def join_asset_file_contents(paths)
+ paths.collect { |path| File.read(asset_file_path(path)) }.join("\n\n")
end
- class StylesheetSources < AssetCollection
- include StylesheetAsset
+ def write_asset_file_contents(joined_asset_path, asset_paths)
+ FileUtils.mkdir_p(File.dirname(joined_asset_path))
+ File.open(joined_asset_path, "w+") { |cache| cache.write(join_asset_file_contents(asset_paths)) }
- EXPANSIONS = {}
+ # Set mtime to the latest of the combined files to allow for
+ # consistent ETag without a shared filesystem.
+ mt = asset_paths.map { |p| File.mtime(asset_file_path(p)) }.max
+ File.utime(mt, mt, joined_asset_path)
+ end
- def self.expansions
- EXPANSIONS
- end
+ def asset_file_path(path)
+ File.join(ASSETS_DIR, path.split('?').first)
+ end
- def expand_sources
- @sources.first == :all ? all_asset_files : validate_sources!
- end
- memoize :expand_sources
+ def collect_asset_files(*path)
+ dir = path.first
- private
- def tag_class
- StylesheetTag
- end
+ Dir[File.join(*path.compact)].collect do |file|
+ file[-(file.size - dir.size - 1)..-1].sub(/\.\w+$/, '')
+ end.sort
end
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/benchmark_helper.rb b/actionpack/lib/action_view/helpers/benchmark_helper.rb
index bd72cda700..61c2cecb04 100644
--- a/actionpack/lib/action_view/helpers/benchmark_helper.rb
+++ b/actionpack/lib/action_view/helpers/benchmark_helper.rb
@@ -18,16 +18,37 @@ module ActionView
# That would add something like "Process data files (345.2ms)" to the log,
# which you can then use to compare timings when optimizing your code.
#
- # You may give an optional logger level as the second argument
+ # You may give an optional logger level as the :level option.
# (:debug, :info, :warn, :error); the default value is :info.
- def benchmark(message = "Benchmarking", level = :info)
+ #
+ # <% benchmark "Low-level files", :level => :debug do %>
+ # <%= lowlevel_files_operation %>
+ # <% end %>
+ #
+ # Finally, you can pass true as the third argument to silence all log activity
+ # inside the block. This is great for boiling down a noisy block to just a single statement:
+ #
+ # <% benchmark "Process data files", :level => :info, :silence => true do %>
+ # <%= expensive_and_chatty_files_operation %>
+ # <% end %>
+ def benchmark(message = "Benchmarking", options = {})
if controller.logger
- seconds = Benchmark.realtime { yield }
- controller.logger.send(level, "#{message} (#{'%.1f' % (seconds * 1000)}ms)")
+ if options.is_a?(Symbol)
+ ActiveSupport::Deprecation.warn("use benchmark('#{message}', :level => :#{options}) instead", caller)
+ options = { :level => options, :silence => false }
+ else
+ options.assert_valid_keys(:level, :silence)
+ options[:level] ||= :info
+ end
+
+ result = nil
+ ms = Benchmark.ms { result = options[:silence] ? controller.logger.silence { yield } : yield }
+ controller.logger.send(options[:level], '%s (%.1fms)' % [ message, ms ])
+ result
else
yield
end
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 22108dd99d..4305617ac8 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -111,7 +111,7 @@ module ActionView
#
# ==== Options
# * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
- # "2" instead of "February").
+ # "2" instead of "February").
# * <tt>:use_short_month</tt> - Set to true if you want to use the abbreviated month name instead of the full
# name (e.g. "Feb" instead of "February").
# * <tt>:add_month_number</tt> - Set to true if you want to show both, the month's number and name (e.g.
@@ -136,6 +136,10 @@ module ActionView
# dates.
# * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
# * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
+ # * <tt>:prompt</tt> - Set to true (for a generic prompt), a prompt string or a hash of prompt strings
+ # for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
+ # Setting this option prepends a select option with a generic prompt (Day, Month, Year, Hour, Minute, Seconds)
+ # or the given prompt string.
#
# If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
#
@@ -171,6 +175,9 @@ module ActionView
# # that will have a default day of 20.
# date_select("credit_card", "bill_due", :default => { :day => 20 })
#
+ # # Generates a date select with custom prompts
+ # date_select("post", "written_on", :prompt => { :day => 'Select day', :month => 'Select month', :year => 'Select year' })
+ #
# The selects are prepared for multi-parameter assignment to an Active Record object.
#
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
@@ -210,6 +217,11 @@ module ActionView
# # You can set the :minute_step to 15 which will give you: 00, 15, 30 and 45.
# time_select 'game', 'game_time', {:minute_step => 15}
#
+ # # Creates a time select tag with a custom prompt. Use :prompt => true for generic prompts.
+ # time_select("post", "written_on", :prompt => {:hour => 'Choose hour', :minute => 'Choose minute', :second => 'Choose seconds'})
+ # time_select("post", "written_on", :prompt => {:hour => true}) # generic prompt for hours
+ # time_select("post", "written_on", :prompt => true) # generic prompts for all
+ #
# The selects are prepared for multi-parameter assignment to an Active Record object.
#
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
@@ -241,6 +253,11 @@ module ActionView
# # as the written_on attribute.
# datetime_select("post", "written_on", :discard_type => true)
#
+ # # Generates a datetime select with a custom prompt. Use :prompt=>true for generic prompts.
+ # datetime_select("post", "written_on", :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
+ # datetime_select("post", "written_on", :prompt => {:hour => true}) # generic prompt for hours
+ # datetime_select("post", "written_on", :prompt => true) # generic prompts for all
+ #
# The selects are prepared for multi-parameter assignment to an Active Record object.
def datetime_select(object_name, method, options = {}, html_options = {})
InstanceTag.new(object_name, method, self, options.delete(:object)).to_datetime_select_tag(options, html_options)
@@ -285,6 +302,11 @@ module ActionView
# # prefixed with 'payday' rather than 'date'
# select_datetime(my_date_time, :prefix => 'payday')
#
+ # # Generates a datetime select with a custom prompt. Use :prompt=>true for generic prompts.
+ # select_datetime(my_date_time, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
+ # select_datetime(my_date_time, :prompt => {:hour => true}) # generic prompt for hours
+ # select_datetime(my_date_time, :prompt => true) # generic prompts for all
+ #
def select_datetime(datetime = Time.current, options = {}, html_options = {})
DateTimeSelector.new(datetime, options, html_options).select_datetime
end
@@ -321,6 +343,11 @@ module ActionView
# # prefixed with 'payday' rather than 'date'
# select_date(my_date, :prefix => 'payday')
#
+ # # Generates a date select with a custom prompt. Use :prompt=>true for generic prompts.
+ # select_date(my_date, :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
+ # select_date(my_date, :prompt => {:hour => true}) # generic prompt for hours
+ # select_date(my_date, :prompt => true) # generic prompts for all
+ #
def select_date(date = Date.current, options = {}, html_options = {})
DateTimeSelector.new(date, options, html_options).select_date
end
@@ -352,6 +379,11 @@ module ActionView
# # separated by ':' and includes an input for seconds
# select_time(my_time, :time_separator => ':', :include_seconds => true)
#
+ # # Generates a time select with a custom prompt. Use :prompt=>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
+ # select_time(my_time, :prompt => true) # generic prompts for all
+ #
def select_time(datetime = Time.current, options = {}, html_options = {})
DateTimeSelector.new(datetime, options, html_options).select_time
end
@@ -373,6 +405,10 @@ module ActionView
# # that is named 'interval' rather than 'second'
# select_second(my_time, :field_name => 'interval')
#
+ # # Generates a select field for seconds with a custom prompt. Use :prompt=>true for a
+ # # generic prompt.
+ # select_minute(14, :prompt => 'Choose seconds')
+ #
def select_second(datetime, options = {}, html_options = {})
DateTimeSelector.new(datetime, options, html_options).select_second
end
@@ -395,6 +431,10 @@ module ActionView
# # that is named 'stride' rather than 'second'
# select_minute(my_time, :field_name => 'stride')
#
+ # # Generates a select field for minutes with a custom prompt. Use :prompt=>true for a
+ # # generic prompt.
+ # select_minute(14, :prompt => 'Choose minutes')
+ #
def select_minute(datetime, options = {}, html_options = {})
DateTimeSelector.new(datetime, options, html_options).select_minute
end
@@ -416,6 +456,10 @@ module ActionView
# # that is named 'stride' rather than 'second'
# select_hour(my_time, :field_name => 'stride')
#
+ # # Generates a select field for hours with a custom prompt. Use :prompt => true for a
+ # # generic prompt.
+ # select_hour(13, :prompt =>'Choose hour')
+ #
def select_hour(datetime, options = {}, html_options = {})
DateTimeSelector.new(datetime, options, html_options).select_hour
end
@@ -437,6 +481,10 @@ module ActionView
# # that is named 'due' rather than 'day'
# select_day(my_time, :field_name => 'due')
#
+ # # Generates a select field for days with a custom prompt. Use :prompt => true for a
+ # # generic prompt.
+ # select_day(5, :prompt => 'Choose day')
+ #
def select_day(date, options = {}, html_options = {})
DateTimeSelector.new(date, options, html_options).select_day
end
@@ -475,6 +523,10 @@ module ActionView
# # will use keys like "Januar", "Marts."
# select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...))
#
+ # # Generates a select field for months with a custom prompt. Use :prompt => true for a
+ # # generic prompt.
+ # select_month(14, :prompt => 'Choose month')
+ #
def select_month(date, options = {}, html_options = {})
DateTimeSelector.new(date, options, html_options).select_month
end
@@ -502,6 +554,10 @@ module ActionView
# # has ascending year values
# select_year(2006, :start_year => 2000, :end_year => 2010)
#
+ # # Generates a select field for years with a custom prompt. Use :prompt => true for a
+ # # generic prompt.
+ # select_year(14, :prompt => 'Choose year')
+ #
def select_year(date, options = {}, html_options = {})
DateTimeSelector.new(date, options, html_options).select_year
end
@@ -764,11 +820,30 @@ module ActionView
select_html = "\n"
select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
+ select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
select_html << select_options_as_html.to_s
content_tag(:select, select_html, select_options) + "\n"
end
+ # Builds a prompt option tag with supplied options or from default options
+ # prompt_option_tag(:month, :prompt => 'Select month')
+ # => "<option value="">Select month</option>"
+ def prompt_option_tag(type, options)
+ default_options = {:year => false, :month => false, :day => false, :hour => false, :minute => false, :second => false}
+
+ case options
+ when Hash
+ prompt = default_options.merge(options)[type.to_sym]
+ when String
+ prompt = options
+ else
+ prompt = I18n.translate(('datetime.prompts.' + type.to_s).to_sym, :locale => @options[:locale])
+ end
+
+ prompt ? content_tag(:option, prompt, :value => '') : ''
+ end
+
# Builds hidden input tag for date part and value
# build_hidden(:year, 2008)
# => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 621e2946b5..a85751c657 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -737,9 +737,13 @@ module ActionView
(field_helpers - %w(label check_box radio_button fields_for)).each do |selector|
src = <<-end_src
- def #{selector}(method, options = {})
- @template.send(#{selector.inspect}, @object_name, method, objectify_options(options))
- end
+ def #{selector}(method, options = {}) # def text_field(method, options = {})
+ @template.send( # @template.send(
+ #{selector.inspect}, # "text_field",
+ @object_name, # @object_name,
+ method, # method,
+ objectify_options(options)) # objectify_options(options))
+ end # end
end_src
class_eval src, __FILE__, __LINE__
end
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 33f8aaf9ed..9ed50a9653 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -201,13 +201,21 @@ module ActionView
# Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the
# the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
- # If +selected+ is specified, the element returning a match on +value_method+ will get the selected option tag.
+ # Example:
+ # options_from_collection_for_select(@people, 'id', 'name')
+ # This will output the same HTML as if you did this:
+ # <option value="#{person.id}">#{person.name}</option>
#
- # Example (call, result). Imagine a loop iterating over each +person+ in <tt>@project.people</tt> to generate an input tag:
- # options_from_collection_for_select(@project.people, "id", "name")
- # <option value="#{person.id}">#{person.name}</option>
+ # This is more often than not used inside a #select_tag like this example:
+ # select_tag 'person', options_from_collection_for_select(@people, 'id', 'name')
#
- # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
+ # If +selected+ is specified, the element returning a match on +value_method+ will get the selected option tag.
+ # Be sure to specify the same class as the +value_method+ when specifying a selected option.
+ # Failure to do this will produce undesired results. Example:
+ # options_from_collection_for_select(@people, 'id', 'name', '1')
+ # Will not select a person with the id of 1 because 1 (an Integer) is not the same as '1' (a string)
+ # options_from_collection_for_select(@people, 'id', 'name', 1)
+ # should produce the desired results.
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
options = collection.map do |element|
[element.send(text_method), element.send(value_method)]
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 7fab3102e7..18a209dcea 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -531,11 +531,6 @@ module ActionView
# is shorthand for
# :with => "'name=' + value"
# This essentially just changes the key of the parameter.
- # <tt>:on</tt>:: Specifies which event handler to observe. By default,
- # it's set to "changed" for text fields and areas and
- # "click" for radio buttons and checkboxes. With this,
- # you can specify it instead to be "blur" or "focus" or
- # any other event.
#
# Additionally, you may specify any of the options documented in the
# <em>Common options</em> section at the top of this document.
@@ -548,11 +543,6 @@ module ActionView
# :url => 'http://example.com/books/edit/1',
# :with => 'title'
#
- # # Sends params: {:book_title => 'Title of the book'} when the focus leaves
- # # the input field.
- # observe_field 'book_title',
- # :url => 'http://example.com/books/edit/1',
- # :on => 'blur'
#
def observe_field(field_id, options = {})
if options[:frequency] && options[:frequency] > 0
@@ -1094,7 +1084,6 @@ module ActionView
javascript << "#{options[:frequency]}, " if options[:frequency]
javascript << "function(element, value) {"
javascript << "#{callback}}"
- javascript << ", '#{options[:on]}'" if options[:on]
javascript << ")"
javascript_tag(javascript)
end
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index 3b301248ff..af8c4d5e21 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -1,4 +1,4 @@
-require 'erb'
+require 'action_view/erb/util'
require 'set'
module ActionView
diff --git a/actionpack/lib/action_view/inline_template.rb b/actionpack/lib/action_view/inline_template.rb
index 5e00cef13f..54efa543c8 100644
--- a/actionpack/lib/action_view/inline_template.rb
+++ b/actionpack/lib/action_view/inline_template.rb
@@ -12,7 +12,7 @@ module ActionView #:nodoc:
private
# Always recompile inline templates
- def recompile?(local_assigns)
+ def recompile?
true
end
end
diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml
index 9542b035aa..a880fd83ef 100644
--- a/actionpack/lib/action_view/locale/en.yml
+++ b/actionpack/lib/action_view/locale/en.yml
@@ -80,6 +80,13 @@
over_x_years:
one: "over 1 year"
other: "over {{count}} years"
+ prompts:
+ year: "Year"
+ month: "Month"
+ day: "Day"
+ hour: "Hour"
+ minute: "Minute"
+ second: "Seconds"
activerecord:
errors:
diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb
index bbc995a340..59e82b98a4 100644
--- a/actionpack/lib/action_view/partials.rb
+++ b/actionpack/lib/action_view/partials.rb
@@ -228,7 +228,7 @@ module ActionView
path = "_#{partial_path}"
end
- _pick_template(path)
+ self.view_paths.find_template(path, self.template_format)
end
memoize :_pick_partial_template
end
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index 9c8b8ade1e..19207e7262 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -2,14 +2,7 @@ module ActionView #:nodoc:
class PathSet < Array #:nodoc:
def self.type_cast(obj)
if obj.is_a?(String)
- if Base.warn_cache_misses && defined?(Rails) && Rails.initialized?
- Base.logger.debug "[PERFORMANCE] Processing view path during a " +
- "request. This an expense disk operation that should be done at " +
- "boot. You can manually process this view path with " +
- "ActionView::Base.process_view_paths(#{obj.inspect}) and set it " +
- "as your view path"
- end
- Path.new(obj)
+ Template::EagerPath.new(obj)
else
obj
end
@@ -39,128 +32,19 @@ module ActionView #:nodoc:
super(*objs.map { |obj| self.class.type_cast(obj) })
end
- class Path #:nodoc:
- attr_reader :path, :paths
- delegate :hash, :inspect, :to => :path
-
- def initialize(path, load = false)
- raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
- @path = path.freeze
- reload! if load
- end
-
- def to_s
- if defined?(RAILS_ROOT)
- path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '')
- else
- path.to_s
- end
- end
-
- def ==(path)
- to_str == path.to_str
- end
-
- def eql?(path)
- to_str == path.to_str
- end
-
- # Returns a ActionView::Template object for the given path string. The
- # input path should be relative to the view path directory,
- # +hello/index.html.erb+. This method also has a special exception to
- # match partial file names without a handler extension. So
- # +hello/index.html+ will match the first template it finds with a
- # known template extension, +hello/index.html.erb+. Template extensions
- # should not be confused with format extensions +html+, +js+, +xml+,
- # etc. A format must be supplied to match a formated file. +hello/index+
- # will never match +hello/index.html.erb+.
- #
- # This method also has two different implementations, one that is "lazy"
- # and makes file system calls every time and the other is cached,
- # "eager" which looks up the template in an in memory index. The "lazy"
- # version is designed for development where you want to automatically
- # find new templates between requests. The "eager" version is designed
- # for production mode and it is much faster but requires more time
- # upfront to build the file index.
- def [](path)
- if loaded?
- @paths[path]
- else
- Dir.glob("#{@path}/#{path}*").each do |file|
- template = create_template(file)
- if path == template.path_without_extension || path == template.path
- return template
- end
- end
- nil
- end
- end
-
- def loaded?
- @loaded ? true : false
- end
-
- def load
- reload! unless loaded?
- self
- end
-
- # Rebuild load path directory cache
- def reload!
- @paths = {}
-
- templates_in_path do |template|
- template.load!
- @paths[template.path] = template
- @paths[template.path_without_extension] ||= template
- end
-
- @paths.freeze
- @loaded = true
- end
-
- private
- def templates_in_path
- (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
- yield create_template(file) unless File.directory?(file)
- end
- end
-
- def create_template(file)
- Template.new(file.split("#{self}/").last, self)
- end
- end
-
- def load
- each { |path| path.load }
- end
+ def find_template(original_template_path, format = nil)
+ return original_template_path if original_template_path.respond_to?(:render)
+ template_path = original_template_path.sub(/^\//, '')
- def reload!
- each { |path| path.reload! }
- end
-
- def [](template_path)
- each do |path|
- if template = path[template_path]
+ each do |load_path|
+ if format && (template = load_path["#{template_path}.#{format}"])
return template
- end
- end
- nil
- end
-
- def find_template(path, *formats)
- if formats && formats.first == :all
- formats = Mime::EXTENSION_LOOKUP.values.map(&:to_sym)
- end
- formats.each do |format|
- if template = self["#{path}.#{format}"]
+ elsif template = load_path[template_path]
return template
end
end
- if template = self[path]
- return template
- end
- nil
+
+ Template.new(original_template_path, self)
end
end
end
diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb
index 7258ad04bf..153e14f68b 100644
--- a/actionpack/lib/action_view/renderable.rb
+++ b/actionpack/lib/action_view/renderable.rb
@@ -4,10 +4,6 @@ module ActionView
module Renderable #:nodoc:
extend ActiveSupport::Memoizable
- def self.included(base)
- @@mutex = Mutex.new
- end
-
def filename
'compiled-template'
end
@@ -22,17 +18,17 @@ module ActionView
end
memoize :compiled_source
+ def method_name_without_locals
+ ['_run', extension, method_segment].compact.join('_')
+ end
+ memoize :method_name_without_locals
+
def render(view, local_assigns = {})
compile(local_assigns)
stack = view.instance_variable_get(:@_render_stack)
stack.push(self)
- # This is only used for TestResponse to set rendered_template
- unless is_a?(InlineTemplate) || view.instance_variable_get(:@_first_render)
- view.instance_variable_set(:@_first_render, self)
- end
-
view.send(:_evaluate_assigns_and_ivars)
view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
@@ -51,9 +47,12 @@ module ActionView
def method_name(local_assigns)
if local_assigns && local_assigns.any?
- local_assigns_keys = "locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
+ method_name = method_name_without_locals.dup
+ method_name << "_locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
+ else
+ method_name = method_name_without_locals
end
- ['_run', extension, method_segment, local_assigns_keys].compact.join('_').to_sym
+ method_name.to_sym
end
private
@@ -61,10 +60,8 @@ module ActionView
def compile(local_assigns)
render_symbol = method_name(local_assigns)
- @@mutex.synchronize do
- if recompile?(render_symbol)
- compile!(render_symbol, local_assigns)
- end
+ if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile?
+ compile!(render_symbol, local_assigns)
end
end
@@ -92,11 +89,8 @@ module ActionView
end
end
- # Method to check whether template compilation is necessary.
- # The template will be compiled if the file has not been compiled yet, or
- # if local_assigns has a new key, which isn't supported by the compiled code yet.
- def recompile?(symbol)
- !Base::CompiledTemplates.method_defined?(symbol) || !loaded?
+ def recompile?
+ false
end
end
end
diff --git a/actionpack/lib/action_view/renderable_partial.rb b/actionpack/lib/action_view/renderable_partial.rb
index d92ff1b8d3..3ea836fa25 100644
--- a/actionpack/lib/action_view/renderable_partial.rb
+++ b/actionpack/lib/action_view/renderable_partial.rb
@@ -25,12 +25,11 @@ module ActionView
end
def render_partial(view, object = nil, local_assigns = {}, as = nil)
- object ||= local_assigns[:object] ||
- local_assigns[variable_name]
+ object ||= local_assigns[:object] || local_assigns[variable_name]
- if view.respond_to?(:controller)
+ if object.nil? && view.respond_to?(:controller)
ivar = :"@#{variable_name}"
- object ||=
+ object =
if view.controller.instance_variable_defined?(ivar)
ActiveSupport::Deprecation::DeprecatedObjectProxy.new(
view.controller.instance_variable_get(ivar),
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index e32b7688d0..88ee07d95b 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -1,9 +1,98 @@
module ActionView #:nodoc:
class Template
+ class Path
+ attr_reader :path, :paths
+ delegate :hash, :inspect, :to => :path
+
+ def initialize(path)
+ raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
+ @path = path.freeze
+ end
+
+ def to_s
+ if defined?(RAILS_ROOT)
+ path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '')
+ else
+ path.to_s
+ end
+ end
+
+ def to_str
+ path.to_str
+ end
+
+ def ==(path)
+ to_str == path.to_str
+ end
+
+ def eql?(path)
+ to_str == path.to_str
+ end
+
+ # Returns a ActionView::Template object for the given path string. The
+ # input path should be relative to the view path directory,
+ # +hello/index.html.erb+. This method also has a special exception to
+ # match partial file names without a handler extension. So
+ # +hello/index.html+ will match the first template it finds with a
+ # known template extension, +hello/index.html.erb+. Template extensions
+ # should not be confused with format extensions +html+, +js+, +xml+,
+ # etc. A format must be supplied to match a formated file. +hello/index+
+ # will never match +hello/index.html.erb+.
+ def [](path)
+ templates_in_path do |template|
+ if template.accessible_paths.include?(path)
+ return template
+ end
+ end
+ nil
+ end
+
+ private
+ def templates_in_path
+ (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
+ yield create_template(file) unless File.directory?(file)
+ end
+ end
+
+ def create_template(file)
+ Template.new(file.split("#{self}/").last, self)
+ end
+ end
+
+ class EagerPath < Path
+ def initialize(path)
+ super
+
+ @paths = {}
+ templates_in_path do |template|
+ template.load!
+ template.accessible_paths.each do |path|
+ @paths[path] = template
+ end
+ end
+ @paths.freeze
+ end
+
+ def [](path)
+ @paths[path]
+ end
+ end
+
extend TemplateHandlers
extend ActiveSupport::Memoizable
include Renderable
+ # Templates that are exempt from layouts
+ @@exempt_from_layout = Set.new([/\.rjs$/])
+
+ # Don't render layouts for templates with the given extensions.
+ def self.exempt_from_layout(*extensions)
+ regexps = extensions.collect do |extension|
+ extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
+ end
+ @@exempt_from_layout.merge(regexps)
+ end
+
attr_accessor :filename, :load_path, :base_path, :name, :format, :extension
delegate :to_s, :to => :path
@@ -17,6 +106,18 @@ module ActionView #:nodoc:
extend RenderablePartial if @name =~ /^_/
end
+ def accessible_paths
+ paths = []
+ paths << path
+ paths << path_without_extension
+ if multipart?
+ formats = format.split(".")
+ paths << "#{path_without_format_and_extension}.#{formats.first}"
+ paths << "#{path_without_format_and_extension}.#{formats.second}"
+ end
+ paths
+ end
+
def format_and_extension
(extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
end
@@ -57,6 +158,10 @@ module ActionView #:nodoc:
end
memoize :relative_path
+ def exempt_from_layout?
+ @@exempt_from_layout.any? { |exempted| path =~ exempted }
+ end
+
def mtime
File.mtime(filename)
end
@@ -88,12 +193,12 @@ module ActionView #:nodoc:
File.mtime(filename) > mtime
end
- def loaded?
- @loaded
+ def recompile?
+ !@cached
end
def load!
- @loaded = true
+ @cached = true
freeze
end
@@ -105,7 +210,7 @@ module ActionView #:nodoc:
def find_full_path(path, load_paths)
load_paths = Array(load_paths) + [nil]
load_paths.each do |load_path|
- file = [load_path, path].compact.join('/')
+ file = load_path ? "#{load_path.to_str}/#{path}" : path
return load_path, file if File.file?(file)
end
raise MissingTemplate.new(load_paths, path)
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 4ab4ed233f..ec337bb05b 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -1,6 +1,29 @@
+require 'active_support/test_case'
+
module ActionView
+ class Base
+ alias_method :initialize_without_template_tracking, :initialize
+ def initialize(*args)
+ @_rendered = { :template => nil, :partials => Hash.new(0) }
+ initialize_without_template_tracking(*args)
+ end
+ end
+
+ module Renderable
+ alias_method :render_without_template_tracking, :render
+ def render(view, local_assigns = {})
+ if respond_to?(:path) && !is_a?(InlineTemplate)
+ rendered = view.instance_variable_get(:@_rendered)
+ rendered[:partials][self] += 1 if is_a?(RenderablePartial)
+ rendered[:template] ||= self
+ end
+ render_without_template_tracking(view, local_assigns)
+ end
+ end
+
class TestCase < ActiveSupport::TestCase
include ActionController::TestCase::Assertions
+ include ActionController::TestProcess
class_inheritable_accessor :helper_class
@@helper_class = nil
@@ -40,11 +63,14 @@ module ActionView
end
class TestController < ActionController::Base
- attr_accessor :request, :response
+ attr_accessor :request, :response, :params
def initialize
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
+
+ @params = {}
+ send(:initialize_current_url)
end
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 51697fda2f..30e2d863d0 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -30,9 +30,10 @@ ActiveSupport::Deprecation.debug = true
ActionController::Base.logger = nil
ActionController::Routing::Routes.reload rescue nil
+ActionController::Base.session_store = nil
+
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
ActionController::Base.view_paths = FIXTURE_LOAD_PATH
-ActionController::Base.view_paths.load
def uses_mocha(test_name)
yield
diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb
index 677d434f9c..6a75e6050d 100644
--- a/actionpack/test/activerecord/active_record_store_test.rb
+++ b/actionpack/test/activerecord/active_record_store_test.rb
@@ -1,140 +1,128 @@
-# These tests exercise CGI::Session::ActiveRecordStore, so you're going to
-# need AR in a sibling directory to AP and have SQLite installed.
require 'active_record_unit'
-module CommonActiveRecordStoreTests
- def test_basics
- s = session_class.new(:session_id => '1234', :data => { 'foo' => 'bar' })
- assert_equal 'bar', s.data['foo']
- assert s.save
- assert_equal 'bar', s.data['foo']
+class ActiveRecordStoreTest < ActionController::IntegrationTest
+ DispatcherApp = ActionController::Dispatcher.new
+ SessionApp = ActiveRecord::SessionStore.new(DispatcherApp,
+ :key => '_session_id')
+ SessionAppWithFixation = ActiveRecord::SessionStore.new(DispatcherApp,
+ :key => '_session_id', :cookie_only => false)
- assert_not_nil t = session_class.find_by_session_id('1234')
- assert_not_nil t.data
- assert_equal 'bar', t.data['foo']
- end
-
- def test_reload_same_session
- @new_session.update
- reloaded = CGI::Session.new(CGI.new, 'session_id' => @new_session.session_id, 'database_manager' => CGI::Session::ActiveRecordStore)
- assert_equal 'bar', reloaded['foo']
- end
-
- def test_tolerates_close_close
- assert_nothing_raised do
- @new_session.close
- @new_session.close
+ class TestController < ActionController::Base
+ def no_session_access
+ head :ok
end
- end
-end
-class ActiveRecordStoreTest < ActiveRecordTestCase
- include CommonActiveRecordStoreTests
+ def set_session_value
+ session[:foo] = params[:foo] || "bar"
+ head :ok
+ end
- def session_class
- CGI::Session::ActiveRecordStore::Session
- end
+ def get_session_value
+ render :text => "foo: #{session[:foo].inspect}"
+ end
- def session_id_column
- "session_id"
+ def rescue_action(e) raise end
end
def setup
- session_class.create_table!
-
- ENV['REQUEST_METHOD'] = 'GET'
- ENV['REQUEST_URI'] = '/'
- CGI::Session::ActiveRecordStore.session_class = session_class
-
- @cgi = CGI.new
- @new_session = CGI::Session.new(@cgi, 'database_manager' => CGI::Session::ActiveRecordStore, 'new_session' => true)
- @new_session['foo'] = 'bar'
+ ActiveRecord::SessionStore.session_class.create_table!
+ @integration_session = open_session(SessionApp)
end
-# this test only applies for eager session saving
-# def test_another_instance
-# @another = CGI::Session.new(@cgi, 'session_id' => @new_session.session_id, 'database_manager' => CGI::Session::ActiveRecordStore)
-# assert_equal @new_session.session_id, @another.session_id
-# end
-
- def test_model_attribute
- assert_kind_of CGI::Session::ActiveRecordStore::Session, @new_session.model
- assert_equal({ 'foo' => 'bar' }, @new_session.model.data)
+ def teardown
+ ActiveRecord::SessionStore.session_class.drop_table!
end
- def test_save_unloaded_session
- c = session_class.connection
- bogus_class = c.quote(ActiveSupport::Base64.encode64("\004\010o:\vBlammo\000"))
- c.insert("INSERT INTO #{session_class.table_name} ('#{session_id_column}', 'data') VALUES ('abcdefghijklmnop', #{bogus_class})")
+ def test_setting_and_getting_session_value
+ with_test_route_set do
+ get '/set_session_value'
+ assert_response :success
+ assert cookies['_session_id']
- sess = session_class.find_by_session_id('abcdefghijklmnop')
- assert_not_nil sess
- assert !sess.loaded?
+ get '/get_session_value'
+ assert_response :success
+ assert_equal 'foo: "bar"', response.body
- # because the session is not loaded, the save should be a no-op. If it
- # isn't, this'll try and unmarshall the bogus class, and should get an error.
- assert_nothing_raised { sess.save }
- end
+ get '/set_session_value', :foo => "baz"
+ assert_response :success
+ assert cookies['_session_id']
- def teardown
- session_class.drop_table!
+ get '/get_session_value'
+ assert_response :success
+ assert_equal 'foo: "baz"', response.body
+ end
end
-end
-class ColumnLimitTest < ActiveRecordTestCase
- def setup
- @session_class = CGI::Session::ActiveRecordStore::Session
- @session_class.create_table!
+ 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 teardown
- @session_class.drop_table!
- end
+ def test_prevents_session_fixation
+ with_test_route_set do
+ get '/set_session_value'
+ assert_response :success
+ assert cookies['_session_id']
- def test_protection_from_data_larger_than_column
- # Can't test this unless there is a limit
- return unless limit = @session_class.data_column_size_limit
- too_big = ':(' * limit
- s = @session_class.new(:session_id => '666', :data => {'foo' => too_big})
- s.data
- assert_raise(ActionController::SessionOverflowError) { s.save }
- end
-end
+ get '/get_session_value'
+ assert_response :success
+ assert_equal 'foo: "bar"', response.body
+ session_id = cookies['_session_id']
+ assert session_id
+
+ reset!
-class DeprecatedActiveRecordStoreTest < ActiveRecordStoreTest
- def session_id_column
- "sessid"
+ get '/set_session_value', :_session_id => session_id, :foo => "baz"
+ assert_response :success
+ assert_equal nil, cookies['_session_id']
+
+ get '/get_session_value', :_session_id => session_id
+ assert_response :success
+ assert_equal 'foo: nil', response.body
+ assert_equal nil, cookies['_session_id']
+ end
end
- def setup
- session_class.connection.execute 'create table old_sessions (id integer primary key, sessid text unique, data text)'
- session_class.table_name = 'old_sessions'
- session_class.send :setup_sessid_compatibility!
+ def test_allows_session_fixation
+ @integration_session = open_session(SessionAppWithFixation)
- ENV['REQUEST_METHOD'] = 'GET'
- CGI::Session::ActiveRecordStore.session_class = session_class
+ with_test_route_set do
+ get '/set_session_value'
+ assert_response :success
+ assert cookies['_session_id']
- @new_session = CGI::Session.new(CGI.new, 'database_manager' => CGI::Session::ActiveRecordStore, 'new_session' => true)
- @new_session['foo'] = 'bar'
- end
+ get '/get_session_value'
+ assert_response :success
+ assert_equal 'foo: "bar"', response.body
+ session_id = cookies['_session_id']
+ assert session_id
- def teardown
- session_class.connection.execute 'drop table old_sessions'
- session_class.table_name = 'sessions'
- end
-end
+ reset!
+ @integration_session = open_session(SessionAppWithFixation)
+
+ get '/set_session_value', :_session_id => session_id, :foo => "baz"
+ assert_response :success
+ assert_equal session_id, cookies['_session_id']
-class SqlBypassActiveRecordStoreTest < ActiveRecordStoreTest
- def session_class
- unless defined? @session_class
- @session_class = CGI::Session::ActiveRecordStore::SqlBypass
- @session_class.connection = CGI::Session::ActiveRecordStore::Session.connection
+ 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
- @session_class
end
- def test_model_attribute
- assert_kind_of CGI::Session::ActiveRecordStore::SqlBypass, @new_session.model
- assert_equal({ 'foo' => 'bar' }, @new_session.model.data)
- end
+ private
+ def with_test_route_set
+ with_routing do |set|
+ set.draw do |map|
+ map.with_options :controller => "active_record_store_test/test" do |c|
+ c.connect "/:action"
+ end
+ end
+ yield
+ end
+ end
end
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index ea56048f37..cb7922efd2 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -326,11 +326,11 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
# check if we were rendered by a file-based template?
def test_rendered_action
process :nothing
- assert_nil @response.rendered_template
+ assert_nil @response.rendered[:template]
process :hello_world
- assert @response.rendered_template
- assert 'hello_world', @response.rendered_template.to_s
+ assert @response.rendered[:template]
+ assert 'hello_world', @response.rendered[:template].to_s
end
# check the redirection location
@@ -500,17 +500,17 @@ class ActionPackHeaderTest < ActionController::TestCase
def test_rendering_xml_sets_content_type
process :hello_xml_world
- assert_equal('application/xml; charset=utf-8', @response.headers['type'])
+ assert_equal('application/xml; charset=utf-8', @response.headers['Content-Type'])
end
def test_rendering_xml_respects_content_type
@response.headers['type'] = 'application/pdf'
process :hello_xml_world
- assert_equal('application/pdf; charset=utf-8', @response.headers['type'])
+ assert_equal('application/pdf; charset=utf-8', @response.headers['Content-Type'])
end
def test_render_text_with_custom_content_type
get :render_text_with_custom_content_type
- assert_equal 'application/rss+xml; charset=utf-8', @response.headers['type']
+ assert_equal 'application/rss+xml; charset=utf-8', @response.headers['Content-Type']
end
end
diff --git a/actionpack/test/controller/addresses_render_test.rb b/actionpack/test/controller/addresses_render_test.rb
index b26cae24fb..556b0593ea 100644
--- a/actionpack/test/controller/addresses_render_test.rb
+++ b/actionpack/test/controller/addresses_render_test.rb
@@ -19,17 +19,14 @@ class AddressesTestController < ActionController::Base
def self.controller_path; "addresses"; end
end
-class AddressesTest < Test::Unit::TestCase
- def setup
- @controller = AddressesTestController.new
+class AddressesTest < ActionController::TestCase
+ tests AddressesTestController
+ def setup
# enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
# a more accurate simulation of what happens in "real life".
@controller.logger = Logger.new(nil)
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
-
@request.host = "www.nextangle.com"
end
diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb
index ed8c4427c9..99c57c0c91 100644
--- a/actionpack/test/controller/assert_select_test.rb
+++ b/actionpack/test/controller/assert_select_test.rb
@@ -248,6 +248,14 @@ class AssertSelectTest < ActionController::TestCase
end
end
+ def test_assert_select_rjs_for_positioned_insert_should_fail_when_mixing_arguments
+ render_rjs do |page|
+ page.insert_html :top, "test1", "<div id=\"1\">foo</div>"
+ page.insert_html :bottom, "test2", "<div id=\"2\">foo</div>"
+ end
+ assert_raises(Assertion) {assert_select_rjs :insert, :top, "test2"}
+ end
+
#
# Test css_select.
#
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index 18d185b264..9523189f41 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -129,6 +129,8 @@ class PerformActionTest < ActionController::TestCase
@response = ActionController::TestResponse.new
@request.host = "www.nextangle.com"
+
+ rescue_action_in_public!
end
def test_get_on_priv_should_show_selector
@@ -164,14 +166,12 @@ class PerformActionTest < ActionController::TestCase
end
end
-class DefaultUrlOptionsTest < Test::Unit::TestCase
- def setup
- @controller = DefaultUrlOptionsController.new
-
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
+class DefaultUrlOptionsTest < ActionController::TestCase
+ tests DefaultUrlOptionsController
+ def setup
@request.host = 'www.example.com'
+ rescue_action_in_public!
end
def test_default_url_options_are_used_if_set
@@ -189,14 +189,12 @@ class DefaultUrlOptionsTest < Test::Unit::TestCase
end
end
-class EmptyUrlOptionsTest < Test::Unit::TestCase
- def setup
- @controller = NonEmptyController.new
-
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
+class EmptyUrlOptionsTest < ActionController::TestCase
+ tests NonEmptyController
+ def setup
@request.host = 'www.example.com'
+ rescue_action_in_public!
end
def test_ensure_url_for_works_as_expected_when_called_with_no_options_if_default_url_options_is_not_set
diff --git a/actionpack/test/controller/benchmark_test.rb b/actionpack/test/controller/benchmark_test.rb
index 608ea5f5a9..f9100a2313 100644
--- a/actionpack/test/controller/benchmark_test.rb
+++ b/actionpack/test/controller/benchmark_test.rb
@@ -11,17 +11,17 @@ class BenchmarkedController < ActionController::Base
end
end
-class BenchmarkTest < Test::Unit::TestCase
+class BenchmarkTest < ActionController::TestCase
+ tests BenchmarkedController
+
class MockLogger
def method_missing(*args)
end
end
def setup
- @controller = BenchmarkedController.new
# benchmark doesn't do anything unless a logger is set
@controller.logger = MockLogger.new
- @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
@request.host = "test.actioncontroller.i"
end
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 7e7f488df6..7f8e47ba58 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -48,6 +48,7 @@ class PageCachingTest < ActionController::TestCase
ActionController::Routing::Routes.draw do |map|
map.main '', :controller => 'posts'
+ map.formatted_posts 'posts.:format', :controller => 'posts'
map.resources :posts
map.connect ':controller/:action/:id'
end
@@ -120,8 +121,7 @@ class PageCachingTest < ActionController::TestCase
[:get, :post, :put, :delete].each do |method|
unless method == :get and status == :ok
define_method "test_shouldnt_cache_#{method}_with_#{status}_status" do
- @request.env['REQUEST_METHOD'] = method.to_s.upcase
- process status
+ send(method, status)
assert_response status
assert_page_not_cached status, "#{method} with #{status} status shouldn't have been cached"
end
@@ -168,7 +168,7 @@ class ActionCachingTestController < ActionController::Base
def forbidden
render :text => "Forbidden"
- headers["Status"] = "403 Forbidden"
+ response.status = "403 Forbidden"
end
def with_layout
diff --git a/actionpack/test/controller/capture_test.rb b/actionpack/test/controller/capture_test.rb
index 5ded6a5d26..6dfa0995eb 100644
--- a/actionpack/test/controller/capture_test.rb
+++ b/actionpack/test/controller/capture_test.rb
@@ -23,17 +23,14 @@ class CaptureController < ActionController::Base
def rescue_action(e) raise end
end
-class CaptureTest < Test::Unit::TestCase
- def setup
- @controller = CaptureController.new
+class CaptureTest < ActionController::TestCase
+ tests CaptureController
+ def setup
# enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
# a more accurate simulation of what happens in "real life".
@controller.logger = Logger.new(nil)
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
-
@request.host = "www.nextangle.com"
end
diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb
index ae71d62e11..32c1757ef9 100644
--- a/actionpack/test/controller/content_type_test.rb
+++ b/actionpack/test/controller/content_type_test.rb
@@ -50,16 +50,13 @@ class ContentTypeController < ActionController::Base
def rescue_action(e) raise end
end
-class ContentTypeTest < Test::Unit::TestCase
- def setup
- @controller = ContentTypeController.new
+class ContentTypeTest < ActionController::TestCase
+ tests ContentTypeController
+ def setup
# enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
# a more accurate simulation of what happens in "real life".
@controller.logger = Logger.new(nil)
-
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
end
def test_render_defaults
diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb
index 5a6fb49861..9508348ca1 100644
--- a/actionpack/test/controller/cookie_test.rb
+++ b/actionpack/test/controller/cookie_test.rb
@@ -1,24 +1,24 @@
require 'abstract_unit'
-class CookieTest < Test::Unit::TestCase
+class CookieTest < ActionController::TestCase
class TestController < ActionController::Base
def authenticate
cookies["user_name"] = "david"
end
def authenticate_for_fourteen_days
- cookies["user_name"] = { "value" => "david", "expires" => Time.local(2005, 10, 10) }
+ cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10,5) }
end
def authenticate_for_fourteen_days_with_symbols
- cookies[:user_name] = { :value => "david", :expires => Time.local(2005, 10, 10) }
+ cookies[:user_name] = { :value => "david", :expires => Time.utc(2005, 10, 10,5) }
end
def set_multiple_cookies
- cookies["user_name"] = { "value" => "david", "expires" => Time.local(2005, 10, 10) }
+ cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10,5) }
cookies["login"] = "XJ-122"
end
-
+
def access_frozen_cookies
cookies["will"] = "work"
end
@@ -36,57 +36,61 @@ class CookieTest < Test::Unit::TestCase
cookies["user_name"] = { :value => "david", :http_only => true }
end
- def rescue_action(e)
- raise unless ActionView::MissingTemplate # No templates here, and we don't care about the output
+ def rescue_action(e)
+ raise unless ActionView::MissingTemplate # No templates here, and we don't care about the output
end
end
- def setup
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
+ tests TestController
- @controller = TestController.new
+ def setup
@request.host = "www.nextangle.com"
end
def test_setting_cookie
get :authenticate
- assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david") ], @response.headers["cookie"]
+ assert_equal ["user_name=david; path=/"], @response.headers["Set-Cookie"]
+ assert_equal({"user_name" => "david"}, @response.cookies)
end
def test_setting_cookie_for_fourteen_days
get :authenticate_for_fourteen_days
- assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "expires" => Time.local(2005, 10, 10)) ], @response.headers["cookie"]
+ assert_equal ["user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"], @response.headers["Set-Cookie"]
+ assert_equal({"user_name" => "david"}, @response.cookies)
end
def test_setting_cookie_for_fourteen_days_with_symbols
get :authenticate_for_fourteen_days_with_symbols
- assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "expires" => Time.local(2005, 10, 10)) ], @response.headers["cookie"]
+ assert_equal ["user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"], @response.headers["Set-Cookie"]
+ assert_equal({"user_name" => "david"}, @response.cookies)
end
def test_setting_cookie_with_http_only
get :authenticate_with_http_only
- assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "http_only" => true) ], @response.headers["cookie"]
- assert_equal CGI::Cookie::new("name" => "user_name", "value" => "david", "path" => "/", "http_only" => true).to_s, @response.headers["cookie"][0].to_s
+ assert_equal ["user_name=david; path=/; HttpOnly"], @response.headers["Set-Cookie"]
+ assert_equal({"user_name" => "david"}, @response.cookies)
end
def test_multiple_cookies
get :set_multiple_cookies
assert_equal 2, @response.cookies.size
+ assert_equal "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT", @response.headers["Set-Cookie"][0]
+ assert_equal "login=XJ-122; path=/", @response.headers["Set-Cookie"][1]
+ assert_equal({"login" => "XJ-122", "user_name" => "david"}, @response.cookies)
end
def test_setting_test_cookie
assert_nothing_raised { get :access_frozen_cookies }
end
-
+
def test_expiring_cookie
get :logout
- assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "", "expires" => Time.at(0)) ], @response.headers["cookie"]
- assert_equal CGI::Cookie::new("name" => "user_name", "value" => "", "expires" => Time.at(0)).value, []
- end
-
+ assert_equal ["user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"], @response.headers["Set-Cookie"]
+ assert_equal({"user_name" => nil}, @response.cookies)
+ end
+
def test_cookiejar_accessor
- @request.cookies["user_name"] = CGI::Cookie.new("name" => "user_name", "value" => "david", "expires" => Time.local(2025, 10, 10))
+ @request.cookies["user_name"] = "david"
@controller.request = @request
jar = ActionController::CookieJar.new(@controller)
assert_equal "david", jar["user_name"]
@@ -94,53 +98,14 @@ class CookieTest < Test::Unit::TestCase
end
def test_cookiejar_accessor_with_array_value
- a = %w{1 2 3}
- @request.cookies["pages"] = CGI::Cookie.new("name" => "pages", "value" => a, "expires" => Time.local(2025, 10, 10))
+ @request.cookies["pages"] = %w{1 2 3}
@controller.request = @request
jar = ActionController::CookieJar.new(@controller)
- assert_equal a, jar["pages"]
+ assert_equal %w{1 2 3}, jar["pages"]
end
-
+
def test_delete_cookie_with_path
get :delete_cookie_with_path
- assert_equal "/beaten", @response.headers["cookie"].first.path
- assert_not_equal "/", @response.headers["cookie"].first.path
- end
-
- def test_cookie_to_s_simple_values
- assert_equal 'myname=myvalue; path=', CGI::Cookie.new('myname', 'myvalue').to_s
- end
-
- def test_cookie_to_s_hash
- cookie_str = CGI::Cookie.new(
- 'name' => 'myname',
- 'value' => 'myvalue',
- 'domain' => 'mydomain',
- 'path' => 'mypath',
- 'expires' => Time.utc(2007, 10, 20),
- 'secure' => true,
- 'http_only' => true).to_s
- assert_equal 'myname=myvalue; domain=mydomain; path=mypath; expires=Sat, 20 Oct 2007 00:00:00 GMT; secure; HttpOnly', cookie_str
- end
-
- def test_cookie_to_s_hash_default_not_secure_not_http_only
- cookie_str = CGI::Cookie.new(
- 'name' => 'myname',
- 'value' => 'myvalue',
- 'domain' => 'mydomain',
- 'path' => 'mypath',
- 'expires' => Time.utc(2007, 10, 20))
- assert cookie_str !~ /secure/
- assert cookie_str !~ /HttpOnly/
- end
-
- def test_cookies_should_not_be_split_on_ampersand_values
- cookies = CGI::Cookie.parse('return_to=http://rubyonrails.org/search?term=api&scope=all&global=true')
- assert_equal({"return_to" => ["http://rubyonrails.org/search?term=api&scope=all&global=true"]}, cookies)
- end
-
- def test_cookies_should_not_be_split_on_values_with_newlines
- cookies = CGI::Cookie.new("name" => "val", "value" => "this\nis\na\ntest")
- assert cookies.size == 1
+ assert_equal ["user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"], @response.headers["Set-Cookie"]
end
end
diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb
index 30dda87bb4..7cd4e71aa1 100644
--- a/actionpack/test/controller/dispatcher_test.rb
+++ b/actionpack/test/controller/dispatcher_test.rb
@@ -32,11 +32,6 @@ class DispatcherTest < Test::Unit::TestCase
dispatch(false)
end
- def test_clears_asset_tag_cache_before_dispatch_if_in_loading_mode
- ActionView::Helpers::AssetTagHelper::AssetTag::Cache.expects(:clear).once
- dispatch(false)
- end
-
def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode
ActionController::Routing::Routes.expects(:reload).never
ActiveSupport::Dependencies.expects(:clear).never
@@ -50,7 +45,7 @@ class DispatcherTest < Test::Unit::TestCase
end
def test_failsafe_response
- Dispatcher.any_instance.expects(:dispatch_unlocked).raises('b00m')
+ Dispatcher.any_instance.expects(:dispatch).raises('b00m')
ActionController::Failsafe.any_instance.expects(:log_failsafe_exception)
assert_nothing_raised do
@@ -96,7 +91,7 @@ class DispatcherTest < Test::Unit::TestCase
private
def dispatch(cache_classes = true)
- Dispatcher.any_instance.stubs(:handle_request).returns([200, {}, 'response'])
+ ActionController::Routing::RouteSet.any_instance.stubs(:call).returns([200, {}, 'response'])
Dispatcher.define_dispatcher_callbacks(cache_classes)
@dispatcher.call({})
end
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index dafa344473..e83fde2349 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -634,9 +634,11 @@ class FilterTest < Test::Unit::TestCase
private
def test_process(controller, action = "show")
+ ActionController::Base.class_eval { include ActionController::ProcessWithTest } unless ActionController::Base < ActionController::ProcessWithTest
request = ActionController::TestRequest.new
request.action = action
- controller.process(request, ActionController::TestResponse.new)
+ controller = controller.new if controller.is_a?(Class)
+ controller.process_with_test(request, ActionController::TestResponse.new)
end
end
@@ -874,8 +876,10 @@ class YieldingAroundFiltersTest < Test::Unit::TestCase
protected
def test_process(controller, action = "show")
+ ActionController::Base.class_eval { include ActionController::ProcessWithTest } unless ActionController::Base < ActionController::ProcessWithTest
request = ActionController::TestRequest.new
request.action = action
- controller.process(request, ActionController::TestResponse.new)
+ controller = controller.new if controller.is_a?(Class)
+ controller.process_with_test(request, ActionController::TestResponse.new)
end
end
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index e562531bf3..d8a892811e 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
-class FlashTest < Test::Unit::TestCase
+class FlashTest < ActionController::TestCase
class TestController < ActionController::Base
def set_flash
flash["that"] = "hello"
@@ -73,11 +73,7 @@ class FlashTest < Test::Unit::TestCase
end
end
- def setup
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- @controller = TestController.new
- end
+ tests TestController
def test_flash
get :set_flash
diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb
new file mode 100644
index 0000000000..d5c8636a9e
--- /dev/null
+++ b/actionpack/test/controller/http_digest_authentication_test.rb
@@ -0,0 +1,73 @@
+require 'abstract_unit'
+
+class HttpDigestAuthenticationTest < Test::Unit::TestCase
+ include ActionController::HttpAuthentication::Digest
+
+ class DummyController
+ attr_accessor :headers, :renders, :request, :response
+
+ def initialize
+ @headers, @renders = {}, []
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ request.session.session_id = "test_session"
+ end
+
+ def render(options)
+ self.renderers << options
+ end
+ end
+
+ def setup
+ @controller = DummyController.new
+ @credentials = {
+ :username => "dhh",
+ :realm => "testrealm@host.com",
+ :nonce => ActionController::HttpAuthentication::Digest.nonce(@controller.request),
+ :qop => "auth",
+ :nc => "00000001",
+ :cnonce => "0a4f113b",
+ :opaque => ActionController::HttpAuthentication::Digest.opaque(@controller.request),
+ :uri => "http://test.host/"
+ }
+ @encoded_credentials = ActionController::HttpAuthentication::Digest.encode_credentials("GET", @credentials, "secret")
+ end
+
+ def test_decode_credentials
+ set_headers
+ assert_equal @credentials, decode_credentials(@controller.request)
+ end
+
+ def test_nonce_format
+ assert_nothing_thrown do
+ validate_nonce(@controller.request, nonce(@controller.request))
+ end
+ end
+
+ def test_authenticate_should_raise_for_nil_password
+ set_headers ActionController::HttpAuthentication::Digest.encode_credentials(:get, @credentials, nil)
+ assert_raise ActionController::HttpAuthentication::Error do
+ authenticate(@controller, @credentials[:realm]) { |user| user == "dhh" && "secret" }
+ end
+ end
+
+ def test_authenticate_should_raise_for_incorrect_password
+ set_headers
+ assert_raise ActionController::HttpAuthentication::Error do
+ authenticate(@controller, @credentials[:realm]) { |user| user == "dhh" && "bad password" }
+ end
+ end
+
+ def test_authenticate_should_not_raise_for_correct_password
+ set_headers
+ assert_nothing_thrown do
+ authenticate(@controller, @credentials[:realm]) { |user| user == "dhh" && "secret" }
+ end
+ end
+
+ private
+ def set_headers(value = @encoded_credentials, name = 'HTTP_AUTHORIZATION', method = "GET")
+ @controller.request.env[name] = value
+ @controller.request.env["REQUEST_METHOD"] = method
+ end
+end
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 4735b2927b..7ac9d97096 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -4,11 +4,29 @@ uses_mocha 'integration' do
class SessionTest < Test::Unit::TestCase
StubApp = lambda { |env|
- [200, {"Content-Type" => "text/html"}, "Hello, World!"]
+ [200, {"Content-Type" => "text/html", "Content-Length" => "13"}, "Hello, World!"]
}
def setup
+ @credentials = {
+ :username => "username",
+ :realm => "MyApp",
+ :nonce => ActionController::HttpAuthentication::Digest.nonce("session_id"),
+ :qop => "auth",
+ :nc => "00000001",
+ :cnonce => "0a4f113b",
+ :opaque => ActionController::HttpAuthentication::Digest.opaque("session_id"),
+ :uri => "/index"
+ }
+
@session = ActionController::Integration::Session.new(StubApp)
+ @session.nonce = @credentials[:nonce]
+ @session.opaque = @credentials[:opaque]
+ @session.realm = @credentials[:realm]
+ end
+
+ def encoded_credentials(method)
+ ActionController::HttpAuthentication::Digest.encode_credentials(method, @credentials, "password")
end
def test_https_bang_works_and_sets_truth_by_default
@@ -30,14 +48,6 @@ class SessionTest < Test::Unit::TestCase
assert_raise(RuntimeError) { @session.follow_redirect! }
end
- def test_follow_redirect_calls_get_and_returns_status
- @session.stubs(:redirect?).returns(true)
- @session.stubs(:headers).returns({"location" => ["www.google.com"]})
- @session.stubs(:status).returns(200)
- @session.expects(:get)
- assert_equal 200, @session.follow_redirect!
- end
-
def test_request_via_redirect_uses_given_method
path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"}
@session.expects(:put).with(path, args, headers)
@@ -140,6 +150,76 @@ class SessionTest < Test::Unit::TestCase
@session.head(path,params,headers)
end
+ def test_get_with_basic
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ expected_headers = headers.merge(:authorization => "Basic dXNlcm5hbWU6cGFzc3dvcmQ=\n")
+ @session.expects(:process).with(:get,path,params,expected_headers)
+ @session.get_with_basic(path,params,headers,'username','password')
+ end
+
+ def test_post_with_basic
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ expected_headers = headers.merge(:authorization => "Basic dXNlcm5hbWU6cGFzc3dvcmQ=\n")
+ @session.expects(:process).with(:post,path,params,expected_headers)
+ @session.post_with_basic(path,params,headers,'username','password')
+ end
+
+ def test_put_with_basic
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ expected_headers = headers.merge(:authorization => "Basic dXNlcm5hbWU6cGFzc3dvcmQ=\n")
+ @session.expects(:process).with(:put,path,params,expected_headers)
+ @session.put_with_basic(path,params,headers,'username','password')
+ end
+
+ def test_delete_with_basic
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ expected_headers = headers.merge(:authorization => "Basic dXNlcm5hbWU6cGFzc3dvcmQ=\n")
+ @session.expects(:process).with(:delete,path,params,expected_headers)
+ @session.delete_with_basic(path,params,headers,'username','password')
+ end
+
+ def test_head_with_basic
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ expected_headers = headers.merge(:authorization => "Basic dXNlcm5hbWU6cGFzc3dvcmQ=\n")
+ @session.expects(:process).with(:head,path,params,expected_headers)
+ @session.head_with_basic(path,params,headers,'username','password')
+ end
+
+ def test_get_with_digest
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ expected_headers = headers.merge(:authorization => encoded_credentials(:get))
+ @session.expects(:process).with(:get,path,params,expected_headers)
+ @session.get_with_digest(path,params,headers,'username','password')
+ end
+
+ def test_post_with_digest
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ expected_headers = headers.merge(:authorization => encoded_credentials(:post))
+ @session.expects(:process).with(:post,path,params,expected_headers)
+ @session.post_with_digest(path,params,headers,'username','password')
+ end
+
+ def test_put_with_digest
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ expected_headers = headers.merge(:authorization => encoded_credentials(:put))
+ @session.expects(:process).with(:put,path,params,expected_headers)
+ @session.put_with_digest(path,params,headers,'username','password')
+ end
+
+ def test_delete_with_digest
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ expected_headers = headers.merge(:authorization => encoded_credentials(:delete))
+ @session.expects(:process).with(:delete,path,params,expected_headers)
+ @session.delete_with_digest(path,params,headers,'username','password')
+ end
+
+ def test_head_with_digest
+ path = "/index"; params = "blah"; headers = {:location => 'blah'}
+ expected_headers = headers.merge(:authorization => encoded_credentials(:head))
+ @session.expects(:process).with(:head,path,params,expected_headers)
+ @session.head_with_digest(path,params,headers,'username','password')
+ end
+
def test_xml_http_request_get
path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge(
@@ -239,8 +319,6 @@ end
class IntegrationProcessTest < ActionController::IntegrationTest
class IntegrationController < ActionController::Base
- session :off
-
def get
respond_to do |format|
format.html { render :text => "OK", :status => 200 }
@@ -323,6 +401,10 @@ class IntegrationProcessTest < ActionController::IntegrationTest
assert_equal "<html><body>You are being <a href=\"http://www.example.com/get\">redirected</a>.</body></html>", response.body
assert_kind_of HTML::Document, html_document
assert_equal 1, request_count
+
+ follow_redirect!
+ assert_response :success
+ assert_equal "/get", path
end
end
@@ -379,4 +461,35 @@ class IntegrationProcessTest < ActionController::IntegrationTest
end
end
+class MetalTest < ActionController::IntegrationTest
+ class Poller
+ def self.call(env)
+ if env["PATH_INFO"] =~ /^\/success/
+ [200, {"Content-Type" => "text/plain", "Content-Length" => "12"}, "Hello World!"]
+ else
+ [404, {"Content-Type" => "text/plain", "Content-Length" => "0"}, '']
+ end
+ end
+ end
+
+ def setup
+ @integration_session = ActionController::Integration::Session.new(Poller)
+ end
+
+ def test_successful_get
+ get "/success"
+ assert_response 200
+ assert_response :success
+ assert_response :ok
+ assert_equal "Hello World!", response.body
+ end
+
+ def test_failed_get
+ get "/failure"
+ assert_response 404
+ assert_response :not_found
+ assert_equal '', response.body
+ end
+end
+
end
diff --git a/actionpack/test/controller/integration_upload_test.rb b/actionpack/test/controller/integration_upload_test.rb
deleted file mode 100644
index b1dd6a6341..0000000000
--- a/actionpack/test/controller/integration_upload_test.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require 'abstract_unit'
-
-unless defined? ApplicationController
- class ApplicationController < ActionController::Base
- end
-end
-
-class UploadTestController < ActionController::Base
- session :off
-
- def update
- SessionUploadTest.last_request_type = ActionController::Base.param_parsers[request.content_type]
- render :text => "got here"
- end
-end
-
-class SessionUploadTest < ActionController::IntegrationTest
- FILES_DIR = File.dirname(__FILE__) + '/../fixtures/multipart'
-
- class << self
- attr_accessor :last_request_type
- end
-
- # def setup
- # @session = ActionController::Integration::Session.new
- # end
- def test_post_with_upload
- uses_mocha "test_post_with_upload" do
- ActiveSupport::Dependencies.stubs(:load?).returns(false)
- with_routing do |set|
- set.draw do |map|
- map.update 'update', :controller => "upload_test", :action => "update", :method => :post
- end
-
- params = { :uploaded_data => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg") }
- post '/update', params, :location => 'blah'
- assert_equal(:multipart_form, SessionUploadTest.last_request_type)
- end
- end
- end
-end
diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb
index 18c01f755c..2f5e830fba 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -146,8 +146,7 @@ class LayoutExceptionRaised < ActionController::TestCase
def test_exception_raised_when_layout_file_not_found
@controller = SetsNonExistentLayoutFile.new
get :hello
- @response.template.class.module_eval { attr_accessor :exception }
- assert_equal ActionView::MissingTemplate, @response.template.exception.class
+ assert_kind_of ActionView::MissingTemplate, @response.template.instance_eval { @exception }
end
end
@@ -165,15 +164,17 @@ class LayoutStatusIsRenderedTest < ActionController::TestCase
end
end
-class LayoutSymlinkedTest < LayoutTest
- layout "symlinked/symlinked_layout"
-end
+unless RUBY_PLATFORM =~ /(:?mswin|mingw|bccwin)/
+ class LayoutSymlinkedTest < LayoutTest
+ layout "symlinked/symlinked_layout"
+ end
-class LayoutSymlinkedIsRenderedTest < ActionController::TestCase
- def test_symlinked_layout_is_rendered
- @controller = LayoutSymlinkedTest.new
- get :hello
- assert_response 200
- assert_equal "layouts/symlinked/symlinked_layout", @response.layout
+ class LayoutSymlinkedIsRenderedTest < ActionController::TestCase
+ def test_symlinked_layout_is_rendered
+ @controller = LayoutSymlinkedTest.new
+ get :hello
+ assert_response 200
+ assert_equal "layouts/symlinked/symlinked_layout", @response.layout
+ end
end
end
diff --git a/actionpack/test/controller/middleware_stack_test.rb b/actionpack/test/controller/middleware_stack_test.rb
new file mode 100644
index 0000000000..5029f5f609
--- /dev/null
+++ b/actionpack/test/controller/middleware_stack_test.rb
@@ -0,0 +1,70 @@
+require 'abstract_unit'
+
+class MiddlewareStackTest < ActiveSupport::TestCase
+ class FooMiddleware; end
+ class BarMiddleware; end
+ class BazMiddleware; end
+
+ def setup
+ @stack = ActionController::MiddlewareStack.new
+ @stack.use FooMiddleware
+ @stack.use BarMiddleware
+ end
+
+ test "use should push middleware as class onto the stack" do
+ assert_difference "@stack.size" do
+ @stack.use BazMiddleware
+ end
+ assert_equal BazMiddleware, @stack.last.klass
+ end
+
+ test "use should push middleware as a string onto the stack" do
+ assert_difference "@stack.size" do
+ @stack.use "MiddlewareStackTest::BazMiddleware"
+ end
+ assert_equal BazMiddleware, @stack.last.klass
+ end
+
+ test "use should push middleware as a symbol onto the stack" do
+ assert_difference "@stack.size" do
+ @stack.use :"MiddlewareStackTest::BazMiddleware"
+ end
+ assert_equal BazMiddleware, @stack.last.klass
+ end
+
+ test "use should push middleware class with arguments onto the stack" do
+ assert_difference "@stack.size" do
+ @stack.use BazMiddleware, true, :foo => "bar"
+ end
+ assert_equal BazMiddleware, @stack.last.klass
+ assert_equal([true, {:foo => "bar"}], @stack.last.args)
+ end
+
+ test "insert inserts middleware at the integer index" do
+ @stack.insert(1, BazMiddleware)
+ assert_equal BazMiddleware, @stack[1].klass
+ end
+
+ test "insert_after inserts middleware after the integer index" do
+ @stack.insert_after(1, BazMiddleware)
+ assert_equal BazMiddleware, @stack[2].klass
+ end
+
+ test "insert_before inserts middleware before another middleware class" do
+ @stack.insert_before(BarMiddleware, BazMiddleware)
+ assert_equal BazMiddleware, @stack[1].klass
+ end
+
+ test "insert_after inserts middleware after another middleware class" do
+ @stack.insert_after(BarMiddleware, BazMiddleware)
+ assert_equal BazMiddleware, @stack[2].klass
+ end
+
+ test "active returns all only enabled middleware" do
+ assert_no_difference "@stack.active.size" do
+ assert_difference "@stack.size" do
+ @stack.use BazMiddleware, :if => lambda { false }
+ end
+ end
+ end
+end
diff --git a/actionpack/test/controller/mime_type_test.rb b/actionpack/test/controller/mime_type_test.rb
index 21ae0419f1..c7faa621d9 100644
--- a/actionpack/test/controller/mime_type_test.rb
+++ b/actionpack/test/controller/mime_type_test.rb
@@ -81,4 +81,13 @@ class MimeTypeTest < Test::Unit::TestCase
assert verified.each { |type| assert Mime.const_get(type.to_s.upcase).verify_request?, "Verifiable Mime Type is not verified: #{type.inspect}" }
assert unverified.each { |type| assert !Mime.const_get(type.to_s.upcase).verify_request?, "Nonverifiable Mime Type is verified: #{type.inspect}" }
end
+
+ def test_regexp_matcher
+ assert Mime::JS =~ "text/javascript"
+ assert Mime::JS =~ "application/javascript"
+ assert Mime::JS !~ "text/html"
+ assert !(Mime::JS !~ "text/javascript")
+ assert !(Mime::JS !~ "application/javascript")
+ assert Mime::HTML =~ 'application/xhtml+xml'
+ end
end
diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/controller/rack_test.rb
index 641ef9626e..8fd004a9e9 100644
--- a/actionpack/test/controller/rack_test.rb
+++ b/actionpack/test/controller/rack_test.rb
@@ -4,7 +4,7 @@ class BaseRackTest < Test::Unit::TestCase
def setup
@env = {
"HTTP_MAX_FORWARDS" => "10",
- "SERVER_NAME" => "glu.ttono.us:8007",
+ "SERVER_NAME" => "glu.ttono.us",
"FCGI_ROLE" => "RESPONDER",
"AUTH_TYPE" => "Basic",
"HTTP_X_FORWARDED_HOST" => "glu.ttono.us",
@@ -43,10 +43,10 @@ class BaseRackTest < Test::Unit::TestCase
"REDIRECT_STATUS" => "200",
"REQUEST_METHOD" => "GET"
}
- @request = ActionController::RackRequest.new(@env)
+ @request = ActionController::Request.new(@env)
# some Nokia phone browsers omit the space after the semicolon separator.
# some developers have grown accustomed to using comma in cookie values.
- @alt_cookie_fmt_request = ActionController::RackRequest.new(@env.merge({"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"}))
+ @alt_cookie_fmt_request = ActionController::Request.new(@env.merge({"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"}))
end
def default_test; end
@@ -145,7 +145,7 @@ class RackRequestTest < BaseRackTest
assert_equal "kevin", @request.remote_user
assert_equal :get, @request.request_method
assert_equal "/dispatch.fcgi", @request.script_name
- assert_equal "glu.ttono.us:8007", @request.server_name
+ assert_equal "glu.ttono.us", @request.server_name
assert_equal 8007, @request.server_port
assert_equal "HTTP/1.1", @request.server_protocol
assert_equal "lighttpd", @request.server_software
@@ -187,29 +187,6 @@ class RackRequestContentTypeTest < BaseRackTest
end
end
-class RackRequestMethodTest < BaseRackTest
- def test_get
- assert_equal :get, @request.request_method
- end
-
- def test_post
- @request.env['REQUEST_METHOD'] = 'POST'
- assert_equal :post, @request.request_method
- end
-
- def test_put
- set_content_data '_method=put'
-
- assert_equal :put, @request.request_method
- end
-
- def test_delete
- set_content_data '_method=delete'
-
- assert_equal :delete, @request.request_method
- end
-end
-
class RackRequestNeedsRewoundTest < BaseRackTest
def test_body_should_be_rewound
data = 'foo'
@@ -218,7 +195,7 @@ class RackRequestNeedsRewoundTest < BaseRackTest
@env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8'
# Read the request body by parsing params.
- request = ActionController::RackRequest.new(@env)
+ request = ActionController::Request.new(@env)
request.request_parameters
# Should have rewound the body.
@@ -229,15 +206,15 @@ end
class RackResponseTest < BaseRackTest
def setup
super
- @response = ActionController::RackResponse.new(@request)
+ @response = ActionController::Response.new
end
def test_simple_output
@response.body = "Hello, World!"
@response.prepare!
- status, headers, body = @response.out
- assert_equal "200 OK", status
+ status, headers, body = @response.to_a
+ assert_equal 200, status
assert_equal({
"Content-Type" => "text/html; charset=utf-8",
"Cache-Control" => "private, max-age=0, must-revalidate",
@@ -257,53 +234,36 @@ class RackResponseTest < BaseRackTest
end
@response.prepare!
- status, headers, body = @response.out
- assert_equal "200 OK", status
- assert_equal({"Content-Type" => "text/html; charset=utf-8", "Cache-Control" => "no-cache", "Set-Cookie" => []}, headers)
-
- parts = []
- body.each { |part| parts << part }
- assert_equal ["0", "1", "2", "3", "4"], parts
- end
-
- def test_set_session_cookie
- cookie = CGI::Cookie.new({"name" => "name", "value" => "Josh"})
- @request.cgi.send :instance_variable_set, '@output_cookies', [cookie]
-
- @response.body = "Hello, World!"
- @response.prepare!
-
- status, headers, body = @response.out
- assert_equal "200 OK", status
+ status, headers, body = @response.to_a
+ assert_equal 200, status
assert_equal({
"Content-Type" => "text/html; charset=utf-8",
- "Cache-Control" => "private, max-age=0, must-revalidate",
- "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"',
- "Set-Cookie" => ["name=Josh; path="],
- "Content-Length" => "13"
+ "Content-Length" => "",
+ "Cache-Control" => "no-cache",
+ "Set-Cookie" => []
}, headers)
parts = []
body.each { |part| parts << part }
- assert_equal ["Hello, World!"], parts
+ assert_equal ["0", "1", "2", "3", "4"], parts
end
end
class RackResponseHeadersTest < BaseRackTest
def setup
super
- @response = ActionController::RackResponse.new(@request)
- @response.headers['Status'] = "200 OK"
+ @response = ActionController::Response.new
+ @response.status = "200 OK"
end
def test_content_type
[204, 304].each do |c|
- @response.headers['Status'] = c.to_s
+ @response.status = c.to_s
assert !response_headers.has_key?("Content-Type"), "#{c} should not have Content-Type header"
end
[200, 302, 404, 500].each do |c|
- @response.headers['Status'] = c.to_s
+ @response.status = c.to_s
assert response_headers.has_key?("Content-Type"), "#{c} did not have Content-Type header"
end
end
@@ -315,6 +275,6 @@ class RackResponseHeadersTest < BaseRackTest
private
def response_headers
@response.prepare!
- @response.out[1]
+ @response.to_a[1]
end
end
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index c5496a9af5..8809aa7c34 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -21,6 +21,8 @@ class MockLogger
end
class TestController < ActionController::Base
+ protect_from_forgery
+
class LabellingFormBuilder < ActionView::Helpers::FormBuilder
end
@@ -79,6 +81,10 @@ class TestController < ActionController::Base
render :action => "hello_world"
end
+ def render_action_hello_world_as_string
+ render "hello_world"
+ end
+
def render_action_hello_world_with_symbol
render :action => :hello_world
end
@@ -102,6 +108,12 @@ class TestController < ActionController::Base
render :file => path
end
+ def render_file_as_string_with_instance_variables
+ @secret = 'in the sauce'
+ path = File.expand_path(File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_ivar.erb'))
+ render path
+ end
+
def render_file_not_using_full_path
@secret = 'in the sauce'
render :file => 'test/render_file_with_ivar'
@@ -122,6 +134,11 @@ class TestController < ActionController::Base
render :file => path, :locals => {:secret => 'in the sauce'}
end
+ def render_file_as_string_with_locals
+ path = File.expand_path(File.join(File.dirname(__FILE__), '../fixtures/test/render_file_with_locals.erb'))
+ render path, :locals => {:secret => 'in the sauce'}
+ end
+
def accessing_request_in_template
render :inline => "Hello: <%= request.host %>"
end
@@ -180,10 +197,6 @@ class TestController < ActionController::Base
render :text => "appended"
end
- def render_invalid_args
- render("test/hello")
- end
-
def render_vanilla_js_hello
render :js => "alert('hello')"
end
@@ -193,6 +206,11 @@ class TestController < ActionController::Base
render :template => "test/hello"
end
+ def render_xml_hello_as_string_template
+ @name = "David"
+ render "test/hello"
+ end
+
def render_xml_with_custom_content_type
render :xml => "<blah/>", :content_type => "application/atomsvc+xml"
end
@@ -209,6 +227,10 @@ class TestController < ActionController::Base
# let's just rely on the template
end
+ def blank_response
+ render :text => ' '
+ end
+
def layout_test
render :action => "hello_world"
end
@@ -278,6 +300,14 @@ class TestController < ActionController::Base
render :action => "hello_world", :layout => "standard"
end
+ def layout_test_with_different_layout_and_string_action
+ render "hello_world", :layout => "standard"
+ end
+
+ def layout_test_with_different_layout_and_symbol_action
+ render :hello_world, :layout => "standard"
+ end
+
def rendering_without_layout
render :action => "hello_world", :layout => false
end
@@ -319,6 +349,10 @@ class TestController < ActionController::Base
render :template => "test/hello_world"
end
+ def render_with_explicit_string_template
+ render "test/hello_world"
+ end
+
def render_with_explicit_template_with_locals
render :template => "test/render_file_with_locals", :locals => { :secret => 'area51' }
end
@@ -641,6 +675,7 @@ class TestController < ActionController::Base
"accessing_params_in_template",
"accessing_params_in_template_with_layout",
"render_with_explicit_template",
+ "render_with_explicit_string_template",
"render_js_with_explicit_template",
"render_js_with_explicit_action_template",
"delete_with_js", "update_page", "update_page_with_instance_variables"
@@ -720,6 +755,12 @@ class RenderTest < ActionController::TestCase
assert_template "test/hello_world"
end
+ def test_render_action_hello_world_as_string
+ get :render_action_hello_world_as_string
+ assert_equal "Hello world!", @response.body
+ assert_template "test/hello_world"
+ end
+
def test_render_action_with_symbol
get :render_action_hello_world_with_symbol
assert_template "test/hello_world"
@@ -745,6 +786,11 @@ class RenderTest < ActionController::TestCase
assert_equal "The secret is in the sauce\n", @response.body
end
+ def test_render_file_as_string_with_instance_variables
+ get :render_file_as_string_with_instance_variables
+ assert_equal "The secret is in the sauce\n", @response.body
+ end
+
def test_render_file_not_using_full_path
get :render_file_not_using_full_path
assert_equal "The secret is in the sauce\n", @response.body
@@ -760,6 +806,11 @@ class RenderTest < ActionController::TestCase
assert_equal "The secret is in the sauce\n", @response.body
end
+ def test_render_file_as_string_with_locals
+ get :render_file_as_string_with_locals
+ assert_equal "The secret is in the sauce\n", @response.body
+ end
+
def test_render_file_from_template
get :render_file_from_template
assert_equal "The secret is in the sauce\n", @response.body
@@ -825,10 +876,6 @@ class RenderTest < ActionController::TestCase
assert_equal 'appended', @response.body
end
- def test_attempt_to_render_with_invalid_arguments
- assert_raises(ActionController::RenderError) { get :render_invalid_args }
- end
-
def test_attempt_to_access_object_method
assert_raises(ActionController::UnknownAction, "No action responded to [clone]") { get :clone }
end
@@ -869,6 +916,12 @@ class RenderTest < ActionController::TestCase
assert_equal "application/xml", @response.content_type
end
+ def test_render_xml_as_string_template
+ get :render_xml_hello_as_string_template
+ assert_equal "<html>\n <p>Hello David</p>\n<p>This is grand!</p>\n</html>\n", @response.body
+ assert_equal "application/xml", @response.content_type
+ end
+
def test_render_xml_with_default
get :greeting
assert_equal "<p>This is grand!</p>\n", @response.body
@@ -1008,6 +1061,16 @@ class RenderTest < ActionController::TestCase
assert_equal "<html>Hello world!</html>", @response.body
end
+ def test_layout_test_with_different_layout_and_string_action
+ get :layout_test_with_different_layout_and_string_action
+ assert_equal "<html>Hello world!</html>", @response.body
+ end
+
+ def test_layout_test_with_different_layout_and_symbol_action
+ get :layout_test_with_different_layout_and_symbol_action
+ assert_equal "<html>Hello world!</html>", @response.body
+ end
+
def test_rendering_without_layout
get :rendering_without_layout
assert_equal "Hello world!", @response.body
@@ -1054,6 +1117,11 @@ class RenderTest < ActionController::TestCase
assert_response :success
end
+ def test_render_with_explicit_string_template
+ get :render_with_explicit_string_template
+ assert_equal "<html>Hello world!</html>", @response.body
+ end
+
def test_double_render
assert_raises(ActionController::DoubleRenderError) { get :double_render }
end
@@ -1095,14 +1163,14 @@ class RenderTest < ActionController::TestCase
def test_update_page
get :update_page
assert_template nil
- assert_equal 'text/javascript; charset=utf-8', @response.headers['type']
+ assert_equal 'text/javascript; charset=utf-8', @response.headers['Content-Type']
assert_equal 2, @response.body.split($/).length
end
def test_update_page_with_instance_variables
get :update_page_with_instance_variables
assert_template nil
- assert_equal 'text/javascript; charset=utf-8', @response.headers['type']
+ assert_equal 'text/javascript; charset=utf-8', @response.headers["Content-Type"]
assert_match /balance/, @response.body
assert_match /\$37/, @response.body
end
@@ -1110,7 +1178,7 @@ class RenderTest < ActionController::TestCase
def test_update_page_with_view_method
get :update_page_with_view_method
assert_template nil
- assert_equal 'text/javascript; charset=utf-8', @response.headers['type']
+ assert_equal 'text/javascript; charset=utf-8', @response.headers["Content-Type"]
assert_match /2 people/, @response.body
end
@@ -1143,13 +1211,18 @@ class RenderTest < ActionController::TestCase
def test_head_with_symbolic_status
get :head_with_symbolic_status, :status => "ok"
- assert_equal "200 OK", @response.headers["Status"]
+ assert_equal "200 OK", @response.status
assert_response :ok
get :head_with_symbolic_status, :status => "not_found"
- assert_equal "404 Not Found", @response.headers["Status"]
+ assert_equal "404 Not Found", @response.status
assert_response :not_found
+ get :head_with_symbolic_status, :status => "no_content"
+ assert_equal "204 No Content", @response.status
+ assert !@response.headers.include?('Content-Length')
+ assert_response :no_content
+
ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.each do |status, code|
get :head_with_symbolic_status, :status => status.to_s
assert_equal code, @response.response_code
@@ -1310,21 +1383,28 @@ class RenderTest < ActionController::TestCase
def test_partial_collection_with_spacer
get :partial_collection_with_spacer
assert_equal "Hello: davidonly partialHello: mary", @response.body
+ assert_template :partial => 'test/_partial_only'
+ assert_template :partial => '_customer'
end
def test_partial_collection_shorthand_with_locals
get :partial_collection_shorthand_with_locals
assert_equal "Bonjour: davidBonjour: mary", @response.body
+ assert_template :partial => 'customers/_customer', :count => 2
+ assert_template :partial => '_completely_fake_and_made_up_template_that_cannot_possibly_be_rendered', :count => 0
end
def test_partial_collection_shorthand_with_different_types_of_records
get :partial_collection_shorthand_with_different_types_of_records
assert_equal "Bonjour bad customer: mark0Bonjour good customer: craig1Bonjour bad customer: john2Bonjour good customer: zach3Bonjour good customer: brandon4Bonjour bad customer: dan5", @response.body
+ assert_template :partial => 'good_customers/_good_customer', :count => 3
+ assert_template :partial => 'bad_customers/_bad_customer', :count => 3
end
def test_empty_partial_collection
get :empty_partial_collection
assert_equal " ", @response.body
+ assert_template :partial => false
end
def test_partial_with_hash_object
@@ -1374,6 +1454,11 @@ class EtagRenderTest < ActionController::TestCase
@expected_bang_etag = etag_for(expand_key([:foo, 123]))
end
+ def test_render_blank_body_shouldnt_set_etag
+ get :blank_response
+ assert !@response.etag?
+ end
+
def test_render_200_should_set_etag
get :render_hello_world_from_variable
assert_equal etag_for("hello david"), @response.headers['ETag']
@@ -1390,7 +1475,7 @@ class EtagRenderTest < ActionController::TestCase
def test_render_against_etag_request_should_have_no_content_length_when_match
@request.if_none_match = etag_for("hello david")
get :render_hello_world_from_variable
- assert !@response.headers.has_key?("Content-Length")
+ assert !@response.headers.has_key?("Content-Length"), @response.headers['Content-Length']
end
def test_render_against_etag_request_should_200_when_no_match
diff --git a/actionpack/test/controller/request/json_params_parsing_test.rb b/actionpack/test/controller/request/json_params_parsing_test.rb
new file mode 100644
index 0000000000..a3dde72c4e
--- /dev/null
+++ b/actionpack/test/controller/request/json_params_parsing_test.rb
@@ -0,0 +1,45 @@
+require 'abstract_unit'
+
+class JsonParamsParsingTest < ActionController::IntegrationTest
+ class TestController < ActionController::Base
+ class << self
+ attr_accessor :last_request_parameters
+ end
+
+ def parse
+ self.class.last_request_parameters = request.request_parameters
+ head :ok
+ end
+ end
+
+ def teardown
+ TestController.last_request_parameters = nil
+ end
+
+ test "parses json params for application json" do
+ assert_parses(
+ {"person" => {"name" => "David"}},
+ "{\"person\": {\"name\": \"David\"}}", { 'CONTENT_TYPE' => 'application/json' }
+ )
+ end
+
+ test "parses json params for application jsonrequest" do
+ assert_parses(
+ {"person" => {"name" => "David"}},
+ "{\"person\": {\"name\": \"David\"}}", { 'CONTENT_TYPE' => 'application/jsonrequest' }
+ )
+ end
+
+ private
+ def assert_parses(expected, actual, headers = {})
+ with_routing do |set|
+ set.draw do |map|
+ map.connect ':action', :controller => "json_params_parsing_test/test"
+ end
+
+ post "/parse", actual, headers
+ assert_response :ok
+ assert_equal(expected, TestController.last_request_parameters)
+ end
+ end
+end
diff --git a/actionpack/test/controller/request/multipart_params_parsing_test.rb b/actionpack/test/controller/request/multipart_params_parsing_test.rb
new file mode 100644
index 0000000000..03ab164972
--- /dev/null
+++ b/actionpack/test/controller/request/multipart_params_parsing_test.rb
@@ -0,0 +1,167 @@
+require 'abstract_unit'
+
+class MultipartParamsParsingTest < ActionController::IntegrationTest
+ class TestController < ActionController::Base
+ class << self
+ attr_accessor :last_request_parameters, :last_request_type
+ end
+
+ def parse
+ self.class.last_request_type = ActionController::Base.param_parsers[request.content_type]
+ self.class.last_request_parameters = request.request_parameters
+ head :ok
+ end
+
+ def read
+ render :text => "File: #{params[:uploaded_data].read}"
+ end
+ end
+
+ FIXTURE_PATH = File.dirname(__FILE__) + '/../../fixtures/multipart'
+
+ def teardown
+ TestController.last_request_parameters = nil
+ TestController.last_request_type = nil
+ end
+
+ test "parses single parameter" do
+ assert_equal({ 'foo' => 'bar' }, parse_multipart('single_parameter'))
+ end
+
+ test "parses bracketed parameters" do
+ assert_equal({ 'foo' => { 'baz' => 'bar'}}, parse_multipart('bracketed_param'))
+ end
+
+ test "parses text file" do
+ params = parse_multipart('text_file')
+ assert_equal %w(file foo), params.keys.sort
+ assert_equal 'bar', params['foo']
+
+ file = params['file']
+ assert_kind_of StringIO, file
+ assert_equal 'file.txt', file.original_filename
+ assert_equal "text/plain", file.content_type
+ assert_equal 'contents', file.read
+ end
+
+ test "parses boundary problem file" do
+ params = parse_multipart('boundary_problem_file')
+ assert_equal %w(file foo), params.keys.sort
+
+ file = params['file']
+ foo = params['foo']
+
+ assert_kind_of Tempfile, file
+
+ assert_equal 'file.txt', file.original_filename
+ assert_equal "text/plain", file.content_type
+
+ assert_equal 'bar', foo
+ end
+
+ test "parses large text file" do
+ params = parse_multipart('large_text_file')
+ assert_equal %w(file foo), params.keys.sort
+ assert_equal 'bar', params['foo']
+
+ file = params['file']
+
+ assert_kind_of Tempfile, file
+
+ assert_equal 'file.txt', file.original_filename
+ assert_equal "text/plain", file.content_type
+ assert ('a' * 20480) == file.read
+ end
+
+ test "parses binary file" do
+ params = parse_multipart('binary_file')
+ assert_equal %w(file flowers foo), params.keys.sort
+ assert_equal 'bar', params['foo']
+
+ file = params['file']
+ assert_kind_of StringIO, file
+ assert_equal 'file.csv', file.original_filename
+ assert_nil file.content_type
+ assert_equal 'contents', file.read
+
+ file = params['flowers']
+ assert_kind_of StringIO, file
+ assert_equal 'flowers.jpg', file.original_filename
+ assert_equal "image/jpeg", file.content_type
+ assert_equal 19512, file.size
+ end
+
+ test "parses mixed files" do
+ params = parse_multipart('mixed_files')
+ assert_equal %w(files foo), params.keys.sort
+ assert_equal 'bar', params['foo']
+
+ # Ruby CGI doesn't handle multipart/mixed for us.
+ files = params['files']
+ assert_kind_of String, files
+ files.force_encoding('ASCII-8BIT') if files.respond_to?(:force_encoding)
+ assert_equal 19756, files.size
+ end
+
+ test "uploads and parses parameters" do
+ with_test_routing do
+ params = { :uploaded_data => fixture_file_upload(FIXTURE_PATH + "/mona_lisa.jpg", "image/jpg") }
+ post '/parse', params, :location => 'blah'
+ assert_equal(:multipart_form, TestController.last_request_type)
+ end
+ end
+
+ test "uploads and reads file" do
+ with_test_routing do
+ post '/read', :uploaded_data => fixture_file_upload(FIXTURE_PATH + "/hello.txt", "text/plain")
+ assert_equal "File: Hello", response.body
+ end
+ end
+
+ # The lint wrapper is used in integration tests
+ # instead of a normal StringIO class
+ InputWrapper = Rack::Lint::InputWrapper
+
+ test "parses unwindable stream" do
+ InputWrapper.any_instance.expects(:rewind).raises(Errno::ESPIPE)
+ params = parse_multipart('large_text_file')
+ assert_equal %w(file foo), params.keys.sort
+ assert_equal 'bar', params['foo']
+ end
+
+ test "uploads and reads file with unwindable input" do
+ InputWrapper.any_instance.expects(:rewind).raises(Errno::ESPIPE)
+
+ with_test_routing do
+ post '/read', :uploaded_data => fixture_file_upload(FIXTURE_PATH + "/hello.txt", "text/plain")
+ assert_equal "File: Hello", response.body
+ end
+ end
+
+ private
+ def fixture(name)
+ File.open(File.join(FIXTURE_PATH, name), 'rb') do |file|
+ { "rack.input" => file.read,
+ "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
+ "CONTENT_LENGTH" => file.stat.size.to_s }
+ end
+ end
+
+ def parse_multipart(name)
+ with_test_routing do
+ headers = fixture(name)
+ post "/parse", headers.delete("rack.input"), headers
+ assert_response :ok
+ TestController.last_request_parameters
+ end
+ end
+
+ def with_test_routing
+ with_routing do |set|
+ set.draw do |map|
+ map.connect ':action', :controller => "multipart_params_parsing_test/test"
+ end
+ yield
+ end
+ end
+end
diff --git a/actionpack/test/controller/request/query_string_parsing_test.rb b/actionpack/test/controller/request/query_string_parsing_test.rb
new file mode 100644
index 0000000000..a31e326ddf
--- /dev/null
+++ b/actionpack/test/controller/request/query_string_parsing_test.rb
@@ -0,0 +1,120 @@
+require 'abstract_unit'
+
+class QueryStringParsingTest < ActionController::IntegrationTest
+ class TestController < ActionController::Base
+ class << self
+ attr_accessor :last_query_parameters
+ end
+
+ def parse
+ self.class.last_query_parameters = request.query_parameters
+ head :ok
+ end
+ end
+
+ def teardown
+ TestController.last_query_parameters = nil
+ end
+
+ test "query string" do
+ assert_parses(
+ {"action" => "create_customer", "full_name" => "David Heinemeier Hansson", "customerId" => "1"},
+ "action=create_customer&full_name=David%20Heinemeier%20Hansson&customerId=1"
+ )
+ end
+
+ test "deep query string" do
+ assert_parses(
+ {'x' => {'y' => {'z' => '10'}}},
+ "x[y][z]=10"
+ )
+ end
+
+ test "deep query string with array" do
+ assert_parses({'x' => {'y' => {'z' => ['10']}}}, 'x[y][z][]=10')
+ assert_parses({'x' => {'y' => {'z' => ['10', '5']}}}, 'x[y][z][]=10&x[y][z][]=5')
+ end
+
+ test "deep query string with array of hash" do
+ assert_parses({'x' => {'y' => [{'z' => '10'}]}}, 'x[y][][z]=10')
+ assert_parses({'x' => {'y' => [{'z' => '10', 'w' => '10'}]}}, 'x[y][][z]=10&x[y][][w]=10')
+ assert_parses({'x' => {'y' => [{'z' => '10', 'v' => {'w' => '10'}}]}}, 'x[y][][z]=10&x[y][][v][w]=10')
+ end
+
+ test "deep query string with array of hashes with one pair" do
+ assert_parses({'x' => {'y' => [{'z' => '10'}, {'z' => '20'}]}}, 'x[y][][z]=10&x[y][][z]=20')
+ end
+
+ test "deep query string with array of hashes with multiple pairs" do
+ assert_parses(
+ {'x' => {'y' => [{'z' => '10', 'w' => 'a'}, {'z' => '20', 'w' => 'b'}]}},
+ 'x[y][][z]=10&x[y][][w]=a&x[y][][z]=20&x[y][][w]=b'
+ )
+ end
+
+ test "query string with nil" do
+ assert_parses(
+ { "action" => "create_customer", "full_name" => ''},
+ "action=create_customer&full_name="
+ )
+ end
+
+ test "query string with array" do
+ assert_parses(
+ { "action" => "create_customer", "selected" => ["1", "2", "3"]},
+ "action=create_customer&selected[]=1&selected[]=2&selected[]=3"
+ )
+ end
+
+ test "query string with amps" do
+ assert_parses(
+ { "action" => "create_customer", "name" => "Don't & Does"},
+ "action=create_customer&name=Don%27t+%26+Does"
+ )
+ end
+
+ test "query string with many equal" do
+ assert_parses(
+ { "action" => "create_customer", "full_name" => "abc=def=ghi"},
+ "action=create_customer&full_name=abc=def=ghi"
+ )
+ end
+
+ test "query string without equal" do
+ assert_parses({ "action" => nil }, "action")
+ end
+
+ test "query string with empty key" do
+ assert_parses(
+ { "action" => "create_customer", "full_name" => "David Heinemeier Hansson" },
+ "action=create_customer&full_name=David%20Heinemeier%20Hansson&=Save"
+ )
+ end
+
+ test "query string with many ampersands" do
+ assert_parses(
+ { "action" => "create_customer", "full_name" => "David Heinemeier Hansson"},
+ "&action=create_customer&&&full_name=David%20Heinemeier%20Hansson"
+ )
+ end
+
+ test "unbalanced query string with array" do
+ assert_parses(
+ {'location' => ["1", "2"], 'age_group' => ["2"]},
+ "location[]=1&location[]=2&age_group[]=2"
+ )
+ end
+
+ private
+ def assert_parses(expected, actual)
+ with_routing do |set|
+ set.draw do |map|
+ map.connect ':action', :controller => "query_string_parsing_test/test"
+ end
+
+ get "/parse", actual
+ assert_response :ok
+ assert_equal(expected, TestController.last_query_parameters)
+ end
+ end
+end
diff --git a/actionpack/test/controller/request/xml_params_parsing_test.rb b/actionpack/test/controller/request/xml_params_parsing_test.rb
new file mode 100644
index 0000000000..ee764e726e
--- /dev/null
+++ b/actionpack/test/controller/request/xml_params_parsing_test.rb
@@ -0,0 +1,88 @@
+require 'abstract_unit'
+
+class XmlParamsParsingTest < ActionController::IntegrationTest
+ class TestController < ActionController::Base
+ class << self
+ attr_accessor :last_request_parameters
+ end
+
+ def parse
+ self.class.last_request_parameters = request.request_parameters
+ head :ok
+ end
+ end
+
+ def teardown
+ TestController.last_request_parameters = nil
+ end
+
+ test "parses hash params" do
+ with_test_routing do
+ xml = "<person><name>David</name></person>"
+ post "/parse", xml, default_headers
+ assert_response :ok
+ assert_equal({"person" => {"name" => "David"}}, TestController.last_request_parameters)
+ end
+ end
+
+ test "parses single file" do
+ with_test_routing do
+ xml = "<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{ActiveSupport::Base64.encode64('ABC')}</avatar></person>"
+ post "/parse", xml, default_headers
+ assert_response :ok
+
+ person = TestController.last_request_parameters
+ assert_equal "image/jpg", person['person']['avatar'].content_type
+ assert_equal "me.jpg", person['person']['avatar'].original_filename
+ assert_equal "ABC", person['person']['avatar'].read
+ end
+ end
+
+ test "parses multiple files" do
+ xml = <<-end_body
+ <person>
+ <name>David</name>
+ <avatars>
+ <avatar type='file' name='me.jpg' content_type='image/jpg'>#{ActiveSupport::Base64.encode64('ABC')}</avatar>
+ <avatar type='file' name='you.gif' content_type='image/gif'>#{ActiveSupport::Base64.encode64('DEF')}</avatar>
+ </avatars>
+ </person>
+ end_body
+
+ with_test_routing do
+ post "/parse", xml, default_headers
+ assert_response :ok
+ end
+
+ person = TestController.last_request_parameters
+
+ assert_equal "image/jpg", person['person']['avatars']['avatar'].first.content_type
+ assert_equal "me.jpg", person['person']['avatars']['avatar'].first.original_filename
+ assert_equal "ABC", person['person']['avatars']['avatar'].first.read
+
+ assert_equal "image/gif", person['person']['avatars']['avatar'].last.content_type
+ assert_equal "you.gif", person['person']['avatars']['avatar'].last.original_filename
+ assert_equal "DEF", person['person']['avatars']['avatar'].last.read
+ end
+
+ private
+ def with_test_routing
+ with_routing do |set|
+ set.draw do |map|
+ map.connect ':action', :controller => "xml_params_parsing_test/test"
+ end
+ yield
+ end
+ end
+
+ def default_headers
+ {'CONTENT_TYPE' => 'application/xml'}
+ end
+end
+
+class LegacyXmlParamsParsingTest < XmlParamsParsingTest
+ private
+ def default_headers
+ {'HTTP_X_POST_DATA_FORMAT' => 'xml'}
+ end
+end
diff --git a/actionpack/test/controller/request_test.rb b/actionpack/test/controller/request_test.rb
index 71da50fab0..3256774b6d 100644
--- a/actionpack/test/controller/request_test.rb
+++ b/actionpack/test/controller/request_test.rb
@@ -303,18 +303,16 @@ class RequestTest < ActiveSupport::TestCase
end
def test_allow_method_hacking_on_post
- self.request_method = :post
[:get, :head, :options, :put, :post, :delete].each do |method|
- @request.instance_eval { @parameters = { :_method => method.to_s } ; @request_method = nil }
+ self.request_method = method
@request.request_method(true)
assert_equal(method == :head ? :get : method, @request.method)
end
end
def test_invalid_method_hacking_on_post_raises_exception
- self.request_method = :post
- @request.instance_eval { @parameters = { :_method => :random_method } ; @request_method = nil }
assert_raises(ActionController::UnknownHttpMethod) do
+ self.request_method = :_random_method
@request.request_method(true)
end
end
@@ -393,8 +391,8 @@ class RequestTest < ActiveSupport::TestCase
end
def test_parameters
- @request.instance_eval { @request_parameters = { "foo" => 1 } }
- @request.instance_eval { @query_parameters = { "bar" => 2 } }
+ @request.stubs(:request_parameters).returns({ "foo" => 1 })
+ @request.stubs(:query_parameters).returns({ "bar" => 2 })
assert_equal({"foo" => 1, "bar" => 2}, @request.parameters)
assert_equal({"foo" => 1}, @request.request_parameters)
@@ -409,113 +407,10 @@ class RequestTest < ActiveSupport::TestCase
end
class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase
- def setup
- @query_string = "action=create_customer&full_name=David%20Heinemeier%20Hansson&customerId=1"
- @query_string_with_empty = "action=create_customer&full_name="
- @query_string_with_array = "action=create_customer&selected[]=1&selected[]=2&selected[]=3"
- @query_string_with_amps = "action=create_customer&name=Don%27t+%26+Does"
- @query_string_with_multiple_of_same_name =
- "action=update_order&full_name=Lau%20Taarnskov&products=4&products=2&products=3"
- @query_string_with_many_equal = "action=create_customer&full_name=abc=def=ghi"
- @query_string_without_equal = "action"
- @query_string_with_many_ampersands =
- "&action=create_customer&&&full_name=David%20Heinemeier%20Hansson"
- @query_string_with_empty_key = "action=create_customer&full_name=David%20Heinemeier%20Hansson&=Save"
- end
-
- def test_query_string
- assert_equal(
- { "action" => "create_customer", "full_name" => "David Heinemeier Hansson", "customerId" => "1"},
- ActionController::AbstractRequest.parse_query_parameters(@query_string)
- )
- end
-
- def test_deep_query_string
- expected = {'x' => {'y' => {'z' => '10'}}}
- assert_equal(expected, ActionController::AbstractRequest.parse_query_parameters('x[y][z]=10'))
- end
-
- def test_deep_query_string_with_array
- assert_equal({'x' => {'y' => {'z' => ['10']}}}, ActionController::AbstractRequest.parse_query_parameters('x[y][z][]=10'))
- assert_equal({'x' => {'y' => {'z' => ['10', '5']}}}, ActionController::AbstractRequest.parse_query_parameters('x[y][z][]=10&x[y][z][]=5'))
- end
-
- def test_deep_query_string_with_array_of_hash
- assert_equal({'x' => {'y' => [{'z' => '10'}]}}, ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10'))
- assert_equal({'x' => {'y' => [{'z' => '10', 'w' => '10'}]}}, ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10&x[y][][w]=10'))
- end
-
- def test_deep_query_string_with_array_of_hashes_with_one_pair
- assert_equal({'x' => {'y' => [{'z' => '10'}, {'z' => '20'}]}}, ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10&x[y][][z]=20'))
- assert_equal("10", ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10&x[y][][z]=20')["x"]["y"].first["z"])
- assert_equal("10", ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10&x[y][][z]=20').with_indifferent_access[:x][:y].first[:z])
- end
-
- def test_deep_query_string_with_array_of_hashes_with_multiple_pairs
- assert_equal(
- {'x' => {'y' => [{'z' => '10', 'w' => 'a'}, {'z' => '20', 'w' => 'b'}]}},
- ActionController::AbstractRequest.parse_query_parameters('x[y][][z]=10&x[y][][w]=a&x[y][][z]=20&x[y][][w]=b')
- )
- end
-
- def test_query_string_with_nil
- assert_equal(
- { "action" => "create_customer", "full_name" => ''},
- ActionController::AbstractRequest.parse_query_parameters(@query_string_with_empty)
- )
- end
-
- def test_query_string_with_array
- assert_equal(
- { "action" => "create_customer", "selected" => ["1", "2", "3"]},
- ActionController::AbstractRequest.parse_query_parameters(@query_string_with_array)
- )
- end
-
- def test_query_string_with_amps
- assert_equal(
- { "action" => "create_customer", "name" => "Don't & Does"},
- ActionController::AbstractRequest.parse_query_parameters(@query_string_with_amps)
- )
- end
-
- def test_query_string_with_many_equal
- assert_equal(
- { "action" => "create_customer", "full_name" => "abc=def=ghi"},
- ActionController::AbstractRequest.parse_query_parameters(@query_string_with_many_equal)
- )
- end
-
- def test_query_string_without_equal
- assert_equal(
- { "action" => nil },
- ActionController::AbstractRequest.parse_query_parameters(@query_string_without_equal)
- )
- end
-
- def test_query_string_with_empty_key
- assert_equal(
- { "action" => "create_customer", "full_name" => "David Heinemeier Hansson" },
- ActionController::AbstractRequest.parse_query_parameters(@query_string_with_empty_key)
- )
- end
-
- def test_query_string_with_many_ampersands
- assert_equal(
- { "action" => "create_customer", "full_name" => "David Heinemeier Hansson"},
- ActionController::AbstractRequest.parse_query_parameters(@query_string_with_many_ampersands)
- )
- end
-
def test_unbalanced_query_string_with_array
assert_equal(
{'location' => ["1", "2"], 'age_group' => ["2"]},
- ActionController::AbstractRequest.parse_query_parameters("location[]=1&location[]=2&age_group[]=2")
- )
- assert_equal(
- {'location' => ["1", "2"], 'age_group' => ["2"]},
- ActionController::AbstractRequest.parse_request_parameters({'location[]' => ["1", "2"],
- 'age_group[]' => ["2"]})
+ ActionController::RequestParser.parse_request_parameters({'location[]' => ["1", "2"], 'age_group[]' => ["2"]})
)
end
@@ -527,7 +422,7 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase
expected = { "note" => { "viewers"=>{"viewer"=>[{ "id"=>"1", "type"=>"User"}, {"type"=>"Group", "id"=>"2"} ]} } }
- assert_equal(expected, ActionController::AbstractRequest.parse_request_parameters(query))
+ assert_equal(expected, ActionController::RequestParser.parse_request_parameters(query))
end
def test_parse_params
@@ -566,7 +461,7 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase
}
}
- assert_equal expected_output, ActionController::AbstractRequest.parse_request_parameters(input)
+ assert_equal expected_output, ActionController::RequestParser.parse_request_parameters(input)
end
UploadedStringIO = ActionController::UploadedStringIO
@@ -621,7 +516,7 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase
"text_part" => "abc"
}
- params = ActionController::AbstractRequest.parse_request_parameters(input)
+ params = ActionController::RequestParser.parse_request_parameters(input)
assert_equal expected_output, params
# Lone filenames are preserved.
@@ -652,7 +547,7 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase
"logo" => File.new(File.dirname(__FILE__) + "/rack_test.rb").path,
}
- assert_equal expected_output, ActionController::AbstractRequest.parse_request_parameters(input)
+ assert_equal expected_output, ActionController::RequestParser.parse_request_parameters(input)
end
def test_parse_params_with_array
@@ -660,234 +555,54 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase
expected_output = { "selected" => [ "1", "2", "3" ] }
- assert_equal expected_output, ActionController::AbstractRequest.parse_request_parameters(input)
+ assert_equal expected_output, ActionController::RequestParser.parse_request_parameters(input)
end
def test_parse_params_with_non_alphanumeric_name
input = { "a/b[c]" => %w(d) }
expected = { "a/b" => { "c" => "d" }}
- assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+ assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
end
def test_parse_params_with_single_brackets_in_middle
input = { "a/b[c]d" => %w(e) }
expected = { "a/b" => {} }
- assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+ assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
end
def test_parse_params_with_separated_brackets
input = { "a/b@[c]d[e]" => %w(f) }
expected = { "a/b@" => { }}
- assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+ assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
end
def test_parse_params_with_separated_brackets_and_array
input = { "a/b@[c]d[e][]" => %w(f) }
expected = { "a/b@" => { }}
- assert_equal expected , ActionController::AbstractRequest.parse_request_parameters(input)
+ assert_equal expected , ActionController::RequestParser.parse_request_parameters(input)
end
def test_parse_params_with_unmatched_brackets_and_array
input = { "a/b@[c][d[e][]" => %w(f) }
expected = { "a/b@" => { "c" => { }}}
- assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+ assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
end
def test_parse_params_with_nil_key
input = { nil => nil, "test2" => %w(value1) }
expected = { "test2" => "value1" }
- assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+ assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
end
def test_parse_params_with_array_prefix_and_hashes
input = { "a[][b][c]" => %w(d) }
expected = {"a" => [{"b" => {"c" => "d"}}]}
- assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
+ assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
end
def test_parse_params_with_complex_nesting
input = { "a[][b][c][][d][]" => %w(e) }
expected = {"a" => [{"b" => {"c" => [{"d" => ["e"]}]}}]}
- assert_equal expected, ActionController::AbstractRequest.parse_request_parameters(input)
- end
-end
-
-class MultipartRequestParameterParsingTest < ActiveSupport::TestCase
- FIXTURE_PATH = File.dirname(__FILE__) + '/../fixtures/multipart'
-
- def test_single_parameter
- params = parse_multipart('single_parameter')
- assert_equal({ 'foo' => 'bar' }, params)
- end
-
- def test_bracketed_param
- assert_equal({ 'foo' => { 'baz' => 'bar'}}, parse_multipart('bracketed_param'))
- end
-
- def test_text_file
- params = parse_multipart('text_file')
- assert_equal %w(file foo), params.keys.sort
- assert_equal 'bar', params['foo']
-
- file = params['file']
- assert_kind_of StringIO, file
- assert_equal 'file.txt', file.original_filename
- assert_equal "text/plain", file.content_type
- assert_equal 'contents', file.read
- end
-
- def test_boundary_problem_file
- params = parse_multipart('boundary_problem_file')
- assert_equal %w(file foo), params.keys.sort
-
- file = params['file']
- foo = params['foo']
-
- assert_kind_of Tempfile, file
-
- assert_equal 'file.txt', file.original_filename
- assert_equal "text/plain", file.content_type
-
- assert_equal 'bar', foo
- end
-
- def test_large_text_file
- params = parse_multipart('large_text_file')
- assert_equal %w(file foo), params.keys.sort
- assert_equal 'bar', params['foo']
-
- file = params['file']
-
- assert_kind_of Tempfile, file
-
- assert_equal 'file.txt', file.original_filename
- assert_equal "text/plain", file.content_type
- assert ('a' * 20480) == file.read
- end
-
- uses_mocha "test_no_rewind_stream" do
- def test_no_rewind_stream
- # Ensures that parse_multipart_form_parameters works with streams that cannot be rewound
- file = File.open(File.join(FIXTURE_PATH, 'large_text_file'), 'rb')
- file.expects(:rewind).raises(Errno::ESPIPE)
- params = ActionController::AbstractRequest.parse_multipart_form_parameters(file, 'AaB03x', file.stat.size, {})
- assert_not_equal 0, file.pos # file was not rewound after reading
- end
- end
-
- def test_binary_file
- params = parse_multipart('binary_file')
- assert_equal %w(file flowers foo), params.keys.sort
- assert_equal 'bar', params['foo']
-
- file = params['file']
- assert_kind_of StringIO, file
- assert_equal 'file.csv', file.original_filename
- assert_nil file.content_type
- assert_equal 'contents', file.read
-
- file = params['flowers']
- assert_kind_of StringIO, file
- assert_equal 'flowers.jpg', file.original_filename
- assert_equal "image/jpeg", file.content_type
- assert_equal 19512, file.size
- #assert_equal File.read(File.dirname(__FILE__) + '/../../../activerecord/test/fixtures/flowers.jpg'), file.read
- end
-
- def test_mixed_files
- params = parse_multipart('mixed_files')
- assert_equal %w(files foo), params.keys.sort
- assert_equal 'bar', params['foo']
-
- # Ruby CGI doesn't handle multipart/mixed for us.
- files = params['files']
- assert_kind_of String, files
- files.force_encoding('ASCII-8BIT') if files.respond_to?(:force_encoding)
- assert_equal 19756, files.size
- end
-
- private
- def parse_multipart(name)
- File.open(File.join(FIXTURE_PATH, name), 'rb') do |file|
- params = ActionController::AbstractRequest.parse_multipart_form_parameters(file, 'AaB03x', file.stat.size, {})
- assert_equal 0, file.pos # file was rewound after reading
- params
- end
- end
-end
-
-class XmlParamsParsingTest < ActiveSupport::TestCase
- def test_hash_params
- person = parse_body("<person><name>David</name></person>")[:person]
- assert_kind_of Hash, person
- assert_equal 'David', person['name']
- end
-
- def test_single_file
- person = parse_body("<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{ActiveSupport::Base64.encode64('ABC')}</avatar></person>")
-
- assert_equal "image/jpg", person['person']['avatar'].content_type
- assert_equal "me.jpg", person['person']['avatar'].original_filename
- assert_equal "ABC", person['person']['avatar'].read
- end
-
- def test_multiple_files
- person = parse_body(<<-end_body)
- <person>
- <name>David</name>
- <avatars>
- <avatar type='file' name='me.jpg' content_type='image/jpg'>#{ActiveSupport::Base64.encode64('ABC')}</avatar>
- <avatar type='file' name='you.gif' content_type='image/gif'>#{ActiveSupport::Base64.encode64('DEF')}</avatar>
- </avatars>
- </person>
- end_body
-
- assert_equal "image/jpg", person['person']['avatars']['avatar'].first.content_type
- assert_equal "me.jpg", person['person']['avatars']['avatar'].first.original_filename
- assert_equal "ABC", person['person']['avatars']['avatar'].first.read
-
- assert_equal "image/gif", person['person']['avatars']['avatar'].last.content_type
- assert_equal "you.gif", person['person']['avatars']['avatar'].last.original_filename
- assert_equal "DEF", person['person']['avatars']['avatar'].last.read
- end
-
- private
- def parse_body(body)
- env = { 'rack.input' => StringIO.new(body),
- 'CONTENT_TYPE' => 'application/xml',
- 'CONTENT_LENGTH' => body.size.to_s }
- ActionController::RackRequest.new(env).request_parameters
- end
-end
-
-class LegacyXmlParamsParsingTest < XmlParamsParsingTest
- private
- def parse_body(body)
- env = { 'rack.input' => StringIO.new(body),
- 'HTTP_X_POST_DATA_FORMAT' => 'xml',
- 'CONTENT_LENGTH' => body.size.to_s }
- ActionController::RackRequest.new(env).request_parameters
- end
-end
-
-class JsonParamsParsingTest < ActiveSupport::TestCase
- def test_hash_params_for_application_json
- person = parse_body({:person => {:name => "David"}}.to_json,'application/json')[:person]
- assert_kind_of Hash, person
- assert_equal 'David', person['name']
- end
-
- def test_hash_params_for_application_jsonrequest
- person = parse_body({:person => {:name => "David"}}.to_json,'application/jsonrequest')[:person]
- assert_kind_of Hash, person
- assert_equal 'David', person['name']
+ assert_equal expected, ActionController::RequestParser.parse_request_parameters(input)
end
-
- private
- def parse_body(body,content_type)
- env = { 'rack.input' => StringIO.new(body),
- 'CONTENT_TYPE' => content_type,
- 'CONTENT_LENGTH' => body.size.to_s }
- ActionController::RackRequest.new(env).request_parameters
- end
end
diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb
index 63f9827f4a..9f6b45f065 100644
--- a/actionpack/test/controller/rescue_test.rb
+++ b/actionpack/test/controller/rescue_test.rb
@@ -67,6 +67,11 @@ class RescueController < ActionController::Base
render :text => 'no way'
end
+ before_filter(:only => :before_filter_raises) { raise 'umm nice' }
+
+ def before_filter_raises
+ end
+
def raises
render :text => 'already rendered'
raise "don't panic!"
@@ -154,6 +159,16 @@ class RescueControllerTest < ActionController::TestCase
end
end
+ def test_rescue_exceptions_raised_by_filters
+ with_rails_root FIXTURE_PUBLIC do
+ with_all_requests_local false do
+ get :before_filter_raises
+ end
+ end
+
+ assert_response :internal_server_error
+ end
+
def test_rescue_action_locally_if_all_requests_local
@controller.expects(:local_request?).never
@controller.expects(:rescue_action_locally).with(@exception)
@@ -367,10 +382,21 @@ class RescueControllerTest < ActionController::TestCase
end
def test_rescue_dispatcher_exceptions
- RescueController.process_with_exception(@request, @response, ActionController::RoutingError.new("Route not found"))
+ env = @request.env
+ env["action_controller.rescue.request"] = @request
+ env["action_controller.rescue.response"] = @response
+
+ RescueController.call_with_exception(env, ActionController::RoutingError.new("Route not found"))
assert_equal "no way", @response.body
end
+ def test_rescue_dispatcher_exceptions_without_request_set
+ @request.env['REQUEST_URI'] = '/no_way'
+ response = RescueController.call_with_exception(@request.env, ActionController::RoutingError.new("Route not found"))
+ assert_kind_of ActionController::Response, response
+ assert_equal "no way", response.body
+ end
+
protected
def with_all_requests_local(local = true)
old_local, ActionController::Base.consider_all_requests_local =
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index d5b6bd6b2a..b981119e1e 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -706,7 +706,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
port_string = port == 80 ? '' : ":#{port}"
protocol = options.delete(:protocol) || "http"
- host = options.delete(:host) || "named.route.test"
+ host = options.delete(:host) || "test.host"
anchor = "##{options.delete(:anchor)}" if options.key?(:anchor)
path = routes.generate(options)
@@ -715,27 +715,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
def request
- @request ||= MockRequest.new(:host => "named.route.test", :method => :get)
- end
- end
-
- class MockRequest
- attr_accessor :path, :path_parameters, :host, :subdomains, :domain, :method
-
- def initialize(values={})
- values.each { |key, value| send("#{key}=", value) }
- if values[:host]
- subdomain, self.domain = values[:host].split(/\./, 2)
- self.subdomains = [subdomain]
- end
- end
-
- def protocol
- "http://"
- end
-
- def host_with_port
- (subdomains * '.') + '.' + domain
+ @request ||= ActionController::TestRequest.new
end
end
@@ -900,7 +880,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
def test_basic_named_route
rs.add_named_route :home, '', :controller => 'content', :action => 'list'
x = setup_for_named_route
- assert_equal("http://named.route.test/",
+ assert_equal("http://test.host/",
x.send(:home_url))
end
@@ -908,7 +888,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
rs.add_named_route :home, '', :controller => 'content', :action => 'list'
x = setup_for_named_route
ActionController::Base.relative_url_root = "/foo"
- assert_equal("http://named.route.test/foo/",
+ assert_equal("http://test.host/foo/",
x.send(:home_url))
assert_equal "/foo/", x.send(:home_path)
ActionController::Base.relative_url_root = nil
@@ -917,14 +897,14 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
def test_named_route_with_option
rs.add_named_route :page, 'page/:title', :controller => 'content', :action => 'show_page'
x = setup_for_named_route
- assert_equal("http://named.route.test/page/new%20stuff",
+ assert_equal("http://test.host/page/new%20stuff",
x.send(:page_url, :title => 'new stuff'))
end
def test_named_route_with_default
rs.add_named_route :page, 'page/:title', :controller => 'content', :action => 'show_page', :title => 'AboutPage'
x = setup_for_named_route
- assert_equal("http://named.route.test/page/AboutRails",
+ assert_equal("http://test.host/page/AboutRails",
x.send(:page_url, :title => "AboutRails"))
end
@@ -932,21 +912,21 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
def test_named_route_with_name_prefix
rs.add_named_route :page, 'page', :controller => 'content', :action => 'show_page', :name_prefix => 'my_'
x = setup_for_named_route
- assert_equal("http://named.route.test/page",
+ assert_equal("http://test.host/page",
x.send(:my_page_url))
end
def test_named_route_with_path_prefix
rs.add_named_route :page, 'page', :controller => 'content', :action => 'show_page', :path_prefix => 'my'
x = setup_for_named_route
- assert_equal("http://named.route.test/my/page",
+ assert_equal("http://test.host/my/page",
x.send(:page_url))
end
def test_named_route_with_nested_controller
rs.add_named_route :users, 'admin/user', :controller => 'admin/user', :action => 'index'
x = setup_for_named_route
- assert_equal("http://named.route.test/admin/user",
+ assert_equal("http://test.host/admin/user",
x.send(:users_url))
end
@@ -985,7 +965,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
map.root :controller => "hello"
end
x = setup_for_named_route
- assert_equal("http://named.route.test/", x.send(:root_url))
+ assert_equal("http://test.host/", x.send(:root_url))
assert_equal("/", x.send(:root_path))
end
@@ -1001,7 +981,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
# x.send(:article_url, :title => 'hi')
# )
assert_equal(
- "http://named.route.test/page/2005/6/10/hi",
+ "http://test.host/page/2005/6/10/hi",
x.send(:article_url, :title => 'hi', :day => 10, :year => 2005, :month => 6)
)
end
@@ -1202,7 +1182,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
assert_equal '/test', rs.generate(:controller => 'post', :action => 'show', :year => nil)
x = setup_for_named_route
- assert_equal("http://named.route.test/test",
+ assert_equal("http://test.host/test",
x.send(:blog_url))
end
@@ -1249,7 +1229,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
assert_equal '/', rs.generate(:controller => 'content')
x = setup_for_named_route
- assert_equal("http://named.route.test/",
+ assert_equal("http://test.host/",
x.send(:home_url))
end
@@ -1591,7 +1571,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
def request
- @request ||= MockRequest.new(:host => "named.routes.test", :method => :get)
+ @request ||= ActionController::TestRequest.new
end
def test_generate_extras
@@ -1692,13 +1672,13 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
def test_named_route_url_method
controller = setup_named_route_test
- assert_equal "http://named.route.test/people/5", controller.send(:show_url, :id => 5)
+ assert_equal "http://test.host/people/5", controller.send(:show_url, :id => 5)
assert_equal "/people/5", controller.send(:show_path, :id => 5)
- assert_equal "http://named.route.test/people", controller.send(:index_url)
+ assert_equal "http://test.host/people", controller.send(:index_url)
assert_equal "/people", controller.send(:index_path)
- assert_equal "http://named.route.test/admin/users", controller.send(:users_url)
+ assert_equal "http://test.host/admin/users", controller.send(:users_url)
assert_equal '/admin/users', controller.send(:users_path)
assert_equal '/admin/users', set.generate(controller.send(:hash_for_users_url), {:controller => 'users', :action => 'index'})
end
@@ -1706,28 +1686,28 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
def test_named_route_url_method_with_anchor
controller = setup_named_route_test
- assert_equal "http://named.route.test/people/5#location", controller.send(:show_url, :id => 5, :anchor => 'location')
+ assert_equal "http://test.host/people/5#location", controller.send(:show_url, :id => 5, :anchor => 'location')
assert_equal "/people/5#location", controller.send(:show_path, :id => 5, :anchor => 'location')
- assert_equal "http://named.route.test/people#location", controller.send(:index_url, :anchor => 'location')
+ assert_equal "http://test.host/people#location", controller.send(:index_url, :anchor => 'location')
assert_equal "/people#location", controller.send(:index_path, :anchor => 'location')
- assert_equal "http://named.route.test/admin/users#location", controller.send(:users_url, :anchor => 'location')
+ assert_equal "http://test.host/admin/users#location", controller.send(:users_url, :anchor => 'location')
assert_equal '/admin/users#location', controller.send(:users_path, :anchor => 'location')
- assert_equal "http://named.route.test/people/go/7/hello/joe/5#location",
+ assert_equal "http://test.host/people/go/7/hello/joe/5#location",
controller.send(:multi_url, 7, "hello", 5, :anchor => 'location')
- assert_equal "http://named.route.test/people/go/7/hello/joe/5?baz=bar#location",
+ assert_equal "http://test.host/people/go/7/hello/joe/5?baz=bar#location",
controller.send(:multi_url, 7, "hello", 5, :baz => "bar", :anchor => 'location')
- assert_equal "http://named.route.test/people?baz=bar#location",
+ assert_equal "http://test.host/people?baz=bar#location",
controller.send(:index_url, :baz => "bar", :anchor => 'location')
end
def test_named_route_url_method_with_port
controller = setup_named_route_test
- assert_equal "http://named.route.test:8080/people/5", controller.send(:show_url, 5, :port=>8080)
+ assert_equal "http://test.host:8080/people/5", controller.send(:show_url, 5, :port=>8080)
end
def test_named_route_url_method_with_host
@@ -1737,30 +1717,30 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
def test_named_route_url_method_with_protocol
controller = setup_named_route_test
- assert_equal "https://named.route.test/people/5", controller.send(:show_url, 5, :protocol => "https")
+ assert_equal "https://test.host/people/5", controller.send(:show_url, 5, :protocol => "https")
end
def test_named_route_url_method_with_ordered_parameters
controller = setup_named_route_test
- assert_equal "http://named.route.test/people/go/7/hello/joe/5",
+ assert_equal "http://test.host/people/go/7/hello/joe/5",
controller.send(:multi_url, 7, "hello", 5)
end
def test_named_route_url_method_with_ordered_parameters_and_hash
controller = setup_named_route_test
- assert_equal "http://named.route.test/people/go/7/hello/joe/5?baz=bar",
+ assert_equal "http://test.host/people/go/7/hello/joe/5?baz=bar",
controller.send(:multi_url, 7, "hello", 5, :baz => "bar")
end
def test_named_route_url_method_with_ordered_parameters_and_empty_hash
controller = setup_named_route_test
- assert_equal "http://named.route.test/people/go/7/hello/joe/5",
+ assert_equal "http://test.host/people/go/7/hello/joe/5",
controller.send(:multi_url, 7, "hello", 5, {})
end
def test_named_route_url_method_with_no_positional_arguments
controller = setup_named_route_test
- assert_equal "http://named.route.test/people?baz=bar",
+ assert_equal "http://test.host/people?baz=bar",
controller.send(:index_url, :baz => "bar")
end
@@ -1896,49 +1876,54 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
request.path = "/people"
- request.method = :get
+ request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("index", request.path_parameters[:action])
+ request.recycle!
- request.method = :post
+ request.env["REQUEST_METHOD"] = "POST"
assert_nothing_raised { set.recognize(request) }
assert_equal("create", request.path_parameters[:action])
+ request.recycle!
- request.method = :put
+ request.env["REQUEST_METHOD"] = "PUT"
assert_nothing_raised { set.recognize(request) }
assert_equal("update", request.path_parameters[:action])
+ request.recycle!
- begin
- request.method = :bacon
+ assert_raises(ActionController::UnknownHttpMethod) {
+ request.env["REQUEST_METHOD"] = "BACON"
set.recognize(request)
- flunk 'Should have raised NotImplemented'
- rescue ActionController::NotImplemented => e
- assert_equal [:get, :post, :put, :delete], e.allowed_methods
- end
+ }
+ request.recycle!
request.path = "/people/5"
- request.method = :get
+ request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("show", request.path_parameters[:action])
assert_equal("5", request.path_parameters[:id])
+ request.recycle!
- request.method = :put
+ request.env["REQUEST_METHOD"] = "PUT"
assert_nothing_raised { set.recognize(request) }
assert_equal("update", request.path_parameters[:action])
assert_equal("5", request.path_parameters[:id])
+ request.recycle!
- request.method = :delete
+ request.env["REQUEST_METHOD"] = "DELETE"
assert_nothing_raised { set.recognize(request) }
assert_equal("destroy", request.path_parameters[:action])
assert_equal("5", request.path_parameters[:id])
+ request.recycle!
begin
- request.method = :post
+ request.env["REQUEST_METHOD"] = "POST"
set.recognize(request)
flunk 'Should have raised MethodNotAllowed'
rescue ActionController::MethodNotAllowed => e
assert_equal [:get, :put, :delete], e.allowed_methods
end
+ request.recycle!
ensure
Object.send(:remove_const, :PeopleController)
@@ -1954,13 +1939,13 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
request.path = "/people"
- request.method = :get
+ request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("people", request.path_parameters[:controller])
assert_equal("index", request.path_parameters[:action])
request.path = "/"
- request.method = :get
+ request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("people", request.path_parameters[:controller])
assert_equal("index", request.path_parameters[:action])
@@ -1978,7 +1963,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
request.path = "/articles/2005/11/05/a-very-interesting-article"
- request.method = :get
+ request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("permalink", request.path_parameters[:action])
assert_equal("2005", request.path_parameters[:year])
@@ -2015,17 +2000,19 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
request.path = "/people/5"
- request.method = :get
+ request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("show", request.path_parameters[:action])
assert_equal("5", request.path_parameters[:id])
+ request.recycle!
- request.method = :put
+ request.env["REQUEST_METHOD"] = "PUT"
assert_nothing_raised { set.recognize(request) }
assert_equal("update", request.path_parameters[:action])
+ request.recycle!
request.path = "/people/5.png"
- request.method = :get
+ request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("show", request.path_parameters[:action])
assert_equal("5", request.path_parameters[:id])
@@ -2050,7 +2037,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
set.draw { |map| map.root :controller => "people" }
request.path = ""
- request.method = :get
+ request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("people", request.path_parameters[:controller])
assert_equal("index", request.path_parameters[:action])
@@ -2070,7 +2057,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
request.path = "/api/inventory"
- request.method = :get
+ request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("api/products", request.path_parameters[:controller])
assert_equal("inventory", request.path_parameters[:action])
@@ -2090,7 +2077,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
request.path = "/api"
- request.method = :get
+ request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("api/products", request.path_parameters[:controller])
assert_equal("index", request.path_parameters[:action])
@@ -2110,7 +2097,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
request.path = "/prefix/inventory"
- request.method = :get
+ request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("api/products", request.path_parameters[:controller])
assert_equal("inventory", request.path_parameters[:action])
@@ -2246,7 +2233,7 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end
request.path = "/projects/1/milestones"
- request.method = :get
+ request.env["REQUEST_METHOD"] = "GET"
assert_nothing_raised { set.recognize(request) }
assert_equal("milestones", request.path_parameters[:controller])
assert_equal("index", request.path_parameters[:action])
diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb
index c003abf094..5fc79baa44 100644
--- a/actionpack/test/controller/send_file_test.rb
+++ b/actionpack/test/controller/send_file_test.rb
@@ -19,7 +19,8 @@ class SendFileController < ActionController::Base
def rescue_action(e) raise end
end
-class SendFileTest < Test::Unit::TestCase
+class SendFileTest < ActionController::TestCase
+ tests SendFileController
include TestFileUtils
Mime::Type.register "image/png", :png unless defined? Mime::PNG
@@ -69,6 +70,7 @@ class SendFileTest < Test::Unit::TestCase
assert_equal @controller.file_path, response.headers['X-Sendfile']
assert response.body.blank?
+ assert !response.etag?
end
def test_data
@@ -118,17 +120,42 @@ class SendFileTest < Test::Unit::TestCase
assert_equal 'private', h['Cache-Control']
end
+ def test_send_file_headers_with_mime_lookup_with_symbol
+ options = {
+ :length => 1,
+ :type => :png
+ }
+
+ @controller.headers = {}
+ @controller.send(:send_file_headers!, options)
+
+ headers = @controller.headers
+
+ assert_equal 'image/png', headers['Content-Type']
+ end
+
+
+ def test_send_file_headers_with_bad_symbol
+ options = {
+ :length => 1,
+ :type => :this_type_is_not_registered
+ }
+
+ @controller.headers = {}
+ assert_raises(ArgumentError){ @controller.send(:send_file_headers!, options) }
+ end
+
%w(file data).each do |method|
define_method "test_send_#{method}_status" do
@controller.options = { :stream => false, :status => 500 }
assert_nothing_raised { assert_not_nil process(method) }
- assert_equal '500 Internal Server Error', @response.headers['Status']
+ assert_equal '500 Internal Server Error', @response.status
end
define_method "test_default_send_#{method}_status" do
@controller.options = { :stream => false }
assert_nothing_raised { assert_not_nil process(method) }
- assert_equal ActionController::Base::DEFAULT_RENDER_STATUS_CODE, @response.headers['Status']
+ assert_equal ActionController::Base::DEFAULT_RENDER_STATUS_CODE, @response.status
end
end
end
diff --git a/actionpack/test/controller/session/cookie_store_test.rb b/actionpack/test/controller/session/cookie_store_test.rb
index b5f14acc1f..d349c18d1d 100644
--- a/actionpack/test/controller/session/cookie_store_test.rb
+++ b/actionpack/test/controller/session/cookie_store_test.rb
@@ -1,298 +1,191 @@
require 'abstract_unit'
require 'stringio'
+class CookieStoreTest < ActionController::IntegrationTest
+ SessionKey = '_myapp_session'
+ SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33'
-class CGI::Session::CookieStore
- def ensure_secret_secure_with_test_hax(secret)
- if secret == CookieStoreTest.default_session_options['secret']
- return true
- else
- ensure_secret_secure_without_test_hax(secret)
- end
- end
- alias_method_chain :ensure_secret_secure, :test_hax
-end
+ DispatcherApp = ActionController::Dispatcher.new
+ CookieStoreApp = ActionController::Session::CookieStore.new(DispatcherApp,
+ :key => SessionKey, :secret => SessionSecret)
+ Verifier = ActiveSupport::MessageVerifier.new(SessionSecret, 'SHA1')
-# Expose for tests.
-class CGI
- attr_reader :output_cookies, :output_hidden
+ SignedBar = "BAh7BjoIZm9vIghiYXI%3D--" +
+ "fef868465920f415f2c0652d6910d3af288a0367"
- class Session
- attr_reader :dbman
+ class TestController < ActionController::Base
+ def no_session_access
+ head :ok
+ end
- class CookieStore
- attr_reader :data, :original, :cookie_options
+ def persistent_session_id
+ render :text => session[:session_id]
end
- end
-end
-class CookieStoreTest < Test::Unit::TestCase
- def self.default_session_options
- { 'database_manager' => CGI::Session::CookieStore,
- 'session_key' => '_myapp_session',
- 'secret' => 'Keep it secret; keep it safe.',
- 'no_cookies' => true,
- 'no_hidden' => true,
- 'session_http_only' => true
- }
- end
+ def set_session_value
+ session[:foo] = "bar"
+ render :text => Verifier.generate(session.to_hash)
+ end
- def self.cookies
- { :empty => ['BAgw--0686dcaccc01040f4bd4f35fe160afe9bc04c330', {}],
- :a_one => ['BAh7BiIGYWkG--5689059497d7f122a7119f171aef81dcfd807fec', { 'a' => 1 }],
- :typical => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7BiILbm90aWNlIgxIZXkgbm93--9d20154623b9eeea05c62ab819be0e2483238759', { 'user_id' => 123, 'flash' => { 'notice' => 'Hey now' }}],
- :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA==--bf9785a666d3c4ac09f7fe3353496b437546cfbf', { 'user_id' => 123, 'flash' => {} }]
- }
+ def get_session_value
+ render :text => "foo: #{session[:foo].inspect}"
+ end
+ def call_reset_session
+ reset_session
+ head :ok
+ end
+
+ def raise_data_overflow
+ session[:foo] = 'bye!' * 1024
+ head :ok
+ end
+
+ def rescue_action(e) raise end
end
def setup
- ENV.delete('HTTP_COOKIE')
+ @integration_session = open_session(CookieStoreApp)
end
def test_raises_argument_error_if_missing_session_key
- [nil, ''].each do |blank|
- assert_raise(ArgumentError, blank.inspect) { new_session 'session_key' => blank }
- end
+ assert_raise(ArgumentError, nil.inspect) {
+ ActionController::Session::CookieStore.new(nil,
+ :key => nil, :secret => SessionSecret)
+ }
+
+ assert_raise(ArgumentError, ''.inspect) {
+ ActionController::Session::CookieStore.new(nil,
+ :key => '', :secret => SessionSecret)
+ }
end
def test_raises_argument_error_if_missing_secret
- [nil, ''].each do |blank|
- assert_raise(ArgumentError, blank.inspect) { new_session 'secret' => blank }
- end
- end
+ assert_raise(ArgumentError, nil.inspect) {
+ ActionController::Session::CookieStore.new(nil,
+ :key => SessionKey, :secret => nil)
+ }
- def test_raises_argument_error_if_secret_is_probably_insecure
- ["password", "secret", "12345678901234567890123456789"].each do |blank|
- assert_raise(ArgumentError, blank.inspect) { new_session 'secret' => blank }
- end
+ assert_raise(ArgumentError, ''.inspect) {
+ ActionController::Session::CookieStore.new(nil,
+ :key => SessionKey, :secret => '')
+ }
end
- def test_reconfigures_session_to_omit_id_cookie_and_hidden_field
- new_session do |session|
- assert_equal true, @options['no_hidden']
- assert_equal true, @options['no_cookies']
- end
- end
+ def test_raises_argument_error_if_secret_is_probably_insecure
+ assert_raise(ArgumentError, "password".inspect) {
+ ActionController::Session::CookieStore.new(nil,
+ :key => SessionKey, :secret => "password")
+ }
- def test_restore_unmarshals_missing_cookie_as_empty_hash
- new_session do |session|
- assert_nil session.dbman.data
- assert_nil session['test']
- assert_equal Hash.new, session.dbman.data
- end
- end
+ assert_raise(ArgumentError, "secret".inspect) {
+ ActionController::Session::CookieStore.new(nil,
+ :key => SessionKey, :secret => "secret")
+ }
- def test_restore_unmarshals_good_cookies
- cookies(:empty, :a_one, :typical).each do |value, expected|
- set_cookie! value
- new_session do |session|
- assert_nil session['lazy loads the data hash']
- assert_equal expected, session.dbman.data
- end
- end
+ assert_raise(ArgumentError, "12345678901234567890123456789".inspect) {
+ ActionController::Session::CookieStore.new(nil,
+ :key => SessionKey, :secret => "12345678901234567890123456789")
+ }
end
- def test_restore_deletes_tampered_cookies
- set_cookie! 'a--b'
- new_session do |session|
- assert_raise(CGI::Session::CookieStore::TamperedWithCookie) { session['fail'] }
- assert_cookie_deleted session
- end
+ def test_setting_session_value
+ with_test_route_set do
+ get '/set_session_value'
+ assert_response :success
+ assert_equal ["_myapp_session=#{response.body}; path=/"],
+ headers['Set-Cookie']
+ end
end
- def test_close_doesnt_write_cookie_if_data_is_blank
- new_session do |session|
- assert_no_cookies session
- session.close
- assert_no_cookies session
- end
+ def test_getting_session_value
+ with_test_route_set do
+ cookies[SessionKey] = SignedBar
+ get '/get_session_value'
+ assert_response :success
+ assert_equal 'foo: "bar"', response.body
+ end
end
- def test_close_doesnt_write_cookie_if_data_is_unchanged
- set_cookie! cookie_value(:typical)
- new_session do |session|
- assert_no_cookies session
- session['user_id'] = session['user_id']
- session.close
- assert_no_cookies session
+ def test_disregards_tampered_sessions
+ with_test_route_set do
+ cookies[SessionKey] = "BAh7BjoIZm9vIghiYXI%3D--123456780"
+ get '/get_session_value'
+ assert_response :success
+ assert_equal 'foo: nil', response.body
end
end
def test_close_raises_when_data_overflows
- set_cookie! cookie_value(:empty)
- new_session do |session|
- session['overflow'] = 'bye!' * 1024
- assert_raise(CGI::Session::CookieStore::CookieOverflow) { session.close }
- assert_no_cookies session
- end
- end
-
- def test_close_marshals_and_writes_cookie
- set_cookie! cookie_value(:typical)
- new_session do |session|
- assert_no_cookies session
- session['flash'] = {}
- assert_no_cookies session
- session.close
- assert_equal 1, session.cgi.output_cookies.size
- cookie = session.cgi.output_cookies.first
- assert_cookie cookie, cookie_value(:flashed)
- assert_http_only_cookie cookie
- assert_secure_cookie cookie, false
+ with_test_route_set do
+ assert_raise(ActionController::Session::CookieStore::CookieOverflow) {
+ get '/raise_data_overflow'
+ }
end
end
- def test_writes_non_secure_cookie_by_default
- set_cookie! cookie_value(:typical)
- new_session do |session|
- session['flash'] = {}
- session.close
- cookie = session.cgi.output_cookies.first
- assert_secure_cookie cookie,false
+ def test_doesnt_write_session_cookie_if_session_is_not_accessed
+ with_test_route_set do
+ get '/no_session_access'
+ assert_response :success
+ assert_equal [], headers['Set-Cookie']
end
end
- def test_writes_secure_cookie
- set_cookie! cookie_value(:typical)
- new_session('session_secure'=>true) do |session|
- session['flash'] = {}
- session.close
- cookie = session.cgi.output_cookies.first
- assert_secure_cookie cookie
+ def test_doesnt_write_session_cookie_if_session_is_unchanged
+ with_test_route_set do
+ cookies[SessionKey] = "BAh7BjoIZm9vIghiYXI%3D--" +
+ "fef868465920f415f2c0652d6910d3af288a0367"
+ get '/no_session_access'
+ assert_response :success
+ assert_equal [], headers['Set-Cookie']
end
end
- def test_http_only_cookie_by_default
- set_cookie! cookie_value(:typical)
- new_session do |session|
- session['flash'] = {}
- session.close
- cookie = session.cgi.output_cookies.first
- assert_http_only_cookie cookie
- end
- end
-
- def test_overides_http_only_cookie
- set_cookie! cookie_value(:typical)
- new_session('session_http_only'=>false) do |session|
- session['flash'] = {}
- session.close
- cookie = session.cgi.output_cookies.first
- assert_http_only_cookie cookie, false
- end
- end
+ def test_setting_session_value_after_session_reset
+ with_test_route_set do
+ get '/set_session_value'
+ assert_response :success
+ session_payload = response.body
+ assert_equal ["_myapp_session=#{response.body}; path=/"],
+ headers['Set-Cookie']
- def test_delete_writes_expired_empty_cookie_and_sets_data_to_nil
- set_cookie! cookie_value(:typical)
- new_session do |session|
- assert_no_cookies session
- session.delete
- assert_cookie_deleted session
+ get '/call_reset_session'
+ assert_response :success
+ assert_not_equal [], headers['Set-Cookie']
+ assert_not_equal session_payload, cookies[SessionKey]
- # @data is set to nil so #close doesn't send another cookie.
- session.close
- assert_cookie_deleted session
+ get '/get_session_value'
+ assert_response :success
+ assert_equal 'foo: nil', response.body
end
end
- def test_new_session_doesnt_reuse_deleted_cookie_data
- set_cookie! cookie_value(:typical)
-
- new_session do |session|
- assert_not_nil session['user_id']
- session.delete
-
- # Start a new session using the same CGI instance.
- post_delete_session = CGI::Session.new(session.cgi, self.class.default_session_options)
- assert_nil post_delete_session['user_id']
+ def test_persistent_session_id
+ with_test_route_set do
+ cookies[SessionKey] = SignedBar
+ get '/persistent_session_id'
+ assert_response :success
+ assert_equal response.body.size, 32
+ session_id = response.body
+ get '/persistent_session_id'
+ assert_equal session_id, response.body
+ reset!
+ get '/persistent_session_id'
+ assert_not_equal session_id, response.body
end
end
private
- def assert_no_cookies(session)
- assert_nil session.cgi.output_cookies, session.cgi.output_cookies.inspect
- end
-
- def assert_cookie_deleted(session, message = 'Expected session deletion cookie to be set')
- assert_equal 1, session.cgi.output_cookies.size
- cookie = session.cgi.output_cookies.first
- assert_cookie cookie, nil, 1.year.ago.to_date, "#{message}: #{cookie.name} => #{cookie.value}"
- end
-
- def assert_cookie(cookie, value = nil, expires = nil, message = nil)
- assert_equal '_myapp_session', cookie.name, message
- assert_equal [value].compact, cookie.value, message
- assert_equal expires, cookie.expires ? cookie.expires.to_date : cookie.expires, message
- end
-
- def assert_secure_cookie(cookie,value=true)
- assert cookie.secure==value
- end
-
- def assert_http_only_cookie(cookie,value=true)
- assert cookie.http_only==value
- end
-
- def cookies(*which)
- self.class.cookies.values_at(*which)
- end
-
- def cookie_value(which)
- self.class.cookies[which].first
- end
-
- def set_cookie!(value)
- ENV['HTTP_COOKIE'] = "_myapp_session=#{value}"
- end
-
- def new_session(options = {})
- with_cgi do |cgi|
- assert_nil cgi.output_hidden, "Output hidden params should be empty: #{cgi.output_hidden.inspect}"
- assert_nil cgi.output_cookies, "Output cookies should be empty: #{cgi.output_cookies.inspect}"
-
- @options = self.class.default_session_options.merge(options)
- session = CGI::Session.new(cgi, @options)
- ObjectSpace.undefine_finalizer(session)
-
- assert_nil cgi.output_hidden, "Output hidden params should be empty: #{cgi.output_hidden.inspect}"
- assert_nil cgi.output_cookies, "Output cookies should be empty: #{cgi.output_cookies.inspect}"
-
- yield session if block_given?
- session
+ def with_test_route_set
+ with_routing do |set|
+ set.draw do |map|
+ map.with_options :controller => "cookie_store_test/test" do |c|
+ c.connect "/:action"
+ end
+ end
+ yield
end
end
-
- def with_cgi
- ENV['REQUEST_METHOD'] = 'GET'
- ENV['HTTP_HOST'] = 'example.com'
- ENV['QUERY_STRING'] = ''
-
- cgi = CGI.new('query', StringIO.new(''))
- yield cgi if block_given?
- cgi
- end
-end
-
-
-class CookieStoreWithBlockAsSecretTest < CookieStoreTest
- def self.default_session_options
- CookieStoreTest.default_session_options.merge 'secret' => Proc.new { 'Keep it secret; keep it safe.' }
- end
-end
-
-
-class CookieStoreWithMD5DigestTest < CookieStoreTest
- def self.default_session_options
- CookieStoreTest.default_session_options.merge 'digest' => 'MD5'
- end
-
- def self.cookies
- { :empty => ['BAgw--0415cc0be9579b14afc22ee2d341aa21', {}],
- :a_one => ['BAh7BiIGYWkG--5a0ed962089cc6600ff44168a5d59bc8', { 'a' => 1 }],
- :typical => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7BiILbm90aWNlIgxIZXkgbm93--f426763f6ef435b3738b493600db8d64', { 'user_id' => 123, 'flash' => { 'notice' => 'Hey now' }}],
- :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA==--0af9156650dab044a53a91a4ddec2c51', { 'user_id' => 123, 'flash' => {} }],
- :double_escaped => [CGI.escape('BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA%3D%3D--0af9156650dab044a53a91a4ddec2c51'), { 'user_id' => 123, 'flash' => {} }] }
- end
end
diff --git a/actionpack/test/controller/session/mem_cache_store_test.rb b/actionpack/test/controller/session/mem_cache_store_test.rb
index 9ab927a01f..eb896a344c 100644
--- a/actionpack/test/controller/session/mem_cache_store_test.rb
+++ b/actionpack/test/controller/session/mem_cache_store_test.rb
@@ -1,178 +1,102 @@
require 'abstract_unit'
-class CGI::Session
- def cache
- dbman.instance_variable_get(:@cache)
- end
-end
+# You need to start a memcached server inorder to run these tests
+class MemCacheStoreTest < ActionController::IntegrationTest
+ class TestController < ActionController::Base
+ def no_session_access
+ head :ok
+ end
+
+ def set_session_value
+ session[:foo] = "bar"
+ head :ok
+ end
+ def get_session_value
+ render :text => "foo: #{session[:foo].inspect}"
+ end
-uses_mocha 'MemCacheStore tests' do
-if defined? MemCache::MemCacheError
-
-class MemCacheStoreTest < Test::Unit::TestCase
- SESSION_KEY_RE = /^session:[0-9a-z]+/
- CONN_TEST_KEY = 'connection_test'
- MULTI_TEST_KEY = '0123456789'
- TEST_DATA = 'Hello test'
-
- def self.get_mem_cache_if_available
- begin
- require 'memcache'
- cache = MemCache.new('127.0.0.1')
- # Test availability of the connection
- cache.set(CONN_TEST_KEY, 1)
- unless cache.get(CONN_TEST_KEY) == 1
- puts 'Warning: memcache server available but corrupted.'
- return nil
- end
- rescue LoadError, MemCache::MemCacheError
- return nil
+ def call_reset_session
+ reset_session
+ head :ok
end
- return cache
+
+ def rescue_action(e) raise end
end
- CACHE = get_mem_cache_if_available
+ begin
+ DispatcherApp = ActionController::Dispatcher.new
+ MemCacheStoreApp = ActionController::Session::MemCacheStore.new(
+ DispatcherApp, :key => '_session_id')
- def test_initialization
- assert_raise(ArgumentError) { new_session('session_id' => '!invalid_id') }
- new_session do |s|
- assert_equal Hash.new, s.cache.get('session:' + s.session_id)
+ def setup
+ @integration_session = open_session(MemCacheStoreApp)
end
- end
+ def test_setting_and_getting_session_value
+ with_test_route_set do
+ get '/set_session_value'
+ assert_response :success
+ assert cookies['_session_id']
- def test_storage
- d = rand(0xffff)
- new_session do |s|
- session_key = 'session:' + s.session_id
- unless CACHE
- s.cache.expects(:get).with(session_key) \
- .returns(:test => d)
- s.cache.expects(:set).with(session_key,
- has_entry(:test, d),
- 0)
- end
- s[:test] = d
- s.close
- assert_equal d, s.cache.get(session_key)[:test]
- assert_equal d, s[:test]
- end
- end
-
- def test_deletion
- new_session do |s|
- session_key = 'session:' + s.session_id
- unless CACHE
- s.cache.expects(:delete)
- s.cache.expects(:get).with(session_key) \
- .returns(nil)
+ get '/get_session_value'
+ assert_response :success
+ assert_equal 'foo: "bar"', response.body
end
- s[:test] = rand(0xffff)
- s.delete
- assert_nil s.cache.get(session_key)
end
- end
-
- def test_other_session_retrieval
- new_session do |sa|
- unless CACHE
- sa.cache.expects(:set).with('session:' + sa.session_id,
- has_entry(:test, TEST_DATA),
- 0)
- end
- sa[:test] = TEST_DATA
- sa.close
- new_session('session_id' => sa.session_id) do |sb|
- unless CACHE
- sb.cache.expects(:[]).with('session:' + sb.session_id) \
- .returns(:test => TEST_DATA)
- end
- assert_equal(TEST_DATA, sb[:test])
+ 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
- end
+ def test_prevents_session_fixation
+ with_test_route_set do
+ get '/get_session_value'
+ assert_response :success
+ assert_equal 'foo: nil', response.body
+ session_id = cookies['_session_id']
- def test_multiple_sessions
- s_slots = Array.new(10)
- operation = :write
- last_data = nil
- reads = writes = 0
- 50.times do
- current = rand(10)
- s_slots[current] ||= new_session('session_id' => MULTI_TEST_KEY,
- 'new_session' => true)
- s = s_slots[current]
- case operation
- when :write
- last_data = rand(0xffff)
- unless CACHE
- s.cache.expects(:set).with('session:' + MULTI_TEST_KEY,
- { :test => last_data },
- 0)
- end
- s[:test] = last_data
- s.close
- writes += 1
- when :read
- # Make CGI::Session#[] think there was no data retrieval yet.
- # Normally, the session caches the data during its lifetime.
- s.instance_variable_set(:@data, nil)
- unless CACHE
- s.cache.expects(:[]).with('session:' + MULTI_TEST_KEY) \
- .returns(:test => last_data)
- end
- d = s[:test]
- assert_equal(last_data, d, "OK reads: #{reads}, OK writes: #{writes}")
- reads += 1
+ reset!
+
+ get '/set_session_value', :_session_id => session_id
+ assert_response :success
+ assert_equal nil, cookies['_session_id']
end
- operation = rand(5) == 0 ? :write : :read
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']
+ get '/call_reset_session'
+ assert_response :success
+ assert_not_equal [], headers['Set-Cookie']
- private
- def obtain_session_options
- options = { 'database_manager' => CGI::Session::MemCacheStore,
- 'session_key' => '_test_app_session'
- }
- # if don't have running memcache server we use mock instead
- unless CACHE
- options['cache'] = c = mock
- c.stubs(:[]).with(regexp_matches(SESSION_KEY_RE))
- c.stubs(:get).with(regexp_matches(SESSION_KEY_RE)) \
- .returns(Hash.new)
- c.stubs(:add).with(regexp_matches(SESSION_KEY_RE),
- instance_of(Hash),
- 0)
+ get '/get_session_value'
+ assert_response :success
+ assert_equal 'foo: nil', response.body
+ end
end
- options
+ rescue LoadError, RuntimeError
+ $stderr.puts "Skipping MemCacheStoreTest tests. Start memcached and try again."
end
-
- def new_session(options = {})
- with_cgi do |cgi|
- @options = obtain_session_options.merge(options)
- session = CGI::Session.new(cgi, @options)
- yield session if block_given?
- return session
+ private
+ def with_test_route_set
+ with_routing do |set|
+ set.draw do |map|
+ map.with_options :controller => "mem_cache_store_test/test" do |c|
+ c.connect "/:action"
+ end
+ end
+ yield
+ end
end
- end
-
- def with_cgi
- ENV['REQUEST_METHOD'] = 'GET'
- ENV['HTTP_HOST'] = 'example.com'
- ENV['QUERY_STRING'] = ''
-
- cgi = CGI.new('query', StringIO.new(''))
- yield cgi if block_given?
- cgi
- end
end
-
-end # defined? MemCache
-end # uses_mocha
diff --git a/actionpack/test/controller/session_fixation_test.rb b/actionpack/test/controller/session_fixation_test.rb
deleted file mode 100644
index e8dc8bd295..0000000000
--- a/actionpack/test/controller/session_fixation_test.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-require 'abstract_unit'
-
-class SessionFixationTest < ActionController::IntegrationTest
- class TestController < ActionController::Base
- session :session_key => '_myapp_session_id',
- :secret => CGI::Session.generate_unique_id,
- :except => :default_session_key
-
- session :cookie_only => false,
- :only => :allow_session_fixation
-
- def default_session_key
- render :text => "default_session_key"
- end
-
- def custom_session_key
- render :text => "custom_session_key: #{params[:id]}"
- end
-
- def allow_session_fixation
- render :text => "allow_session_fixation"
- end
-
- def rescue_action(e) raise end
- end
-
- def setup
- @controller = TestController.new
- end
-
- def test_should_be_able_to_make_a_successful_request
- with_test_route_set do
- assert_nothing_raised do
- get '/custom_session_key', :id => "1"
- end
- assert_equal 'custom_session_key: 1', @controller.response.body
- assert_not_nil @controller.session
- end
- end
-
- def test_should_catch_session_fixation_attempt
- with_test_route_set do
- assert_raises(ActionController::RackRequest::SessionFixationAttempt) do
- get '/custom_session_key', :_myapp_session_id => "42"
- end
- assert_nil @controller.session
- end
- end
-
- def test_should_not_catch_session_fixation_attempt_when_cookie_only_setting_is_disabled
- with_test_route_set do
- assert_nothing_raised do
- get '/allow_session_fixation', :_myapp_session_id => "42"
- end
- assert !@controller.response.body.blank?
- assert_not_nil @controller.session
- end
- end
-
- def test_should_catch_session_fixation_attempt_with_default_session_key
- # using the default session_key is not possible with cookie store
- ActionController::Base.session_store = :p_store
-
- with_test_route_set do
- assert_raises ActionController::RackRequest::SessionFixationAttempt do
- get '/default_session_key', :_session_id => "42"
- end
- assert_nil @controller.response
- assert_nil @controller.session
- end
- end
-
- private
- def with_test_route_set
- with_routing do |set|
- set.draw do |map|
- map.with_options :controller => "session_fixation_test/test" do |c|
- c.connect "/:action"
- end
- end
- yield
- end
- end
-end
diff --git a/actionpack/test/controller/session_management_test.rb b/actionpack/test/controller/session_management_test.rb
deleted file mode 100644
index 592b0b549d..0000000000
--- a/actionpack/test/controller/session_management_test.rb
+++ /dev/null
@@ -1,178 +0,0 @@
-require 'abstract_unit'
-
-class SessionManagementTest < Test::Unit::TestCase
- class SessionOffController < ActionController::Base
- session :off
-
- def show
- render :text => "done"
- end
-
- def tell
- render :text => "done"
- end
- end
-
- class SessionOffOnController < ActionController::Base
- session :off
- session :on, :only => :tell
-
- def show
- render :text => "done"
- end
-
- def tell
- render :text => "done"
- end
- end
-
- class TestController < ActionController::Base
- session :off, :only => :show
- session :session_secure => true, :except => :show
- session :off, :only => :conditional,
- :if => Proc.new { |r| r.parameters[:ws] }
-
- def show
- render :text => "done"
- end
-
- def tell
- render :text => "done"
- end
-
- def conditional
- render :text => ">>>#{params[:ws]}<<<"
- end
- end
-
- class SpecializedController < SessionOffController
- session :disabled => false, :only => :something
-
- def something
- render :text => "done"
- end
-
- def another
- render :text => "done"
- end
- end
-
- class AssociationCachingTestController < ActionController::Base
- class ObjectWithAssociationCache
- def initialize
- @cached_associations = false
- end
-
- def fetch_associations
- @cached_associations = true
- end
-
- def clear_association_cache
- @cached_associations = false
- end
-
- def has_cached_associations?
- @cached_associations
- end
- end
-
- def show
- session[:object] = ObjectWithAssociationCache.new
- session[:object].fetch_associations
- if session[:object].has_cached_associations?
- render :text => "has cached associations"
- else
- render :text => "does not have cached associations"
- end
- end
-
- def tell
- if session[:object]
- if session[:object].has_cached_associations?
- render :text => "has cached associations"
- else
- render :text => "does not have cached associations"
- end
- else
- render :text => "there is no object"
- end
- end
- end
-
-
- def setup
- @request, @response = ActionController::TestRequest.new,
- ActionController::TestResponse.new
- end
-
- def test_session_off_globally
- @controller = SessionOffController.new
- get :show
- assert_equal false, @request.session_options
- get :tell
- assert_equal false, @request.session_options
- end
-
- def test_session_off_then_on_globally
- @controller = SessionOffOnController.new
- get :show
- assert_equal false, @request.session_options
- get :tell
- assert_instance_of Hash, @request.session_options
- assert_equal false, @request.session_options[:disabled]
- end
-
- def test_session_off_conditionally
- @controller = TestController.new
- get :show
- assert_equal false, @request.session_options
- get :tell
- assert_instance_of Hash, @request.session_options
- assert @request.session_options[:session_secure]
- end
-
- def test_controller_specialization_overrides_settings
- @controller = SpecializedController.new
- get :something
- assert_instance_of Hash, @request.session_options
- get :another
- assert_equal false, @request.session_options
- end
-
- def test_session_off_with_if
- @controller = TestController.new
- get :conditional
- assert_instance_of Hash, @request.session_options
- get :conditional, :ws => "ws"
- assert_equal false, @request.session_options
- end
-
- def test_session_store_setting
- ActionController::Base.session_store = :drb_store
- assert_equal CGI::Session::DRbStore, ActionController::Base.session_store
-
- if Object.const_defined?(:ActiveRecord)
- ActionController::Base.session_store = :active_record_store
- assert_equal CGI::Session::ActiveRecordStore, ActionController::Base.session_store
- end
- end
-
- def test_process_cleanup_with_session_management_support
- @controller = AssociationCachingTestController.new
- get :show
- assert_equal "has cached associations", @response.body
- get :tell
- assert_equal "does not have cached associations", @response.body
- end
-
- def test_session_is_enabled
- @controller = TestController.new
- get :show
- assert_nothing_raised do
- assert_equal false, @controller.session_enabled?
- end
-
- get :tell
- assert @controller.session_enabled?
- end
-end
diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb
index 4c44ea4205..e89d6bb960 100644
--- a/actionpack/test/controller/webservice_test.rb
+++ b/actionpack/test/controller/webservice_test.rb
@@ -2,8 +2,6 @@ require 'abstract_unit'
class WebServiceTest < ActionController::IntegrationTest
class TestController < ActionController::Base
- session :off
-
def assign_parameters
if params[:full]
render :text => dump_params_keys
diff --git a/actionpack/test/fixtures/multipart/hello.txt b/actionpack/test/fixtures/multipart/hello.txt
new file mode 100644
index 0000000000..5ab2f8a432
--- /dev/null
+++ b/actionpack/test/fixtures/multipart/hello.txt
@@ -0,0 +1 @@
+Hello \ 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 7597927f6d..5e2fc20167 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -38,8 +38,6 @@ class AssetTagHelperTest < ActionView::TestCase
@controller.request = @request
ActionView::Helpers::AssetTagHelper::reset_javascript_include_default
- AssetTag::Cache.clear
- AssetCollection::Cache.clear
end
def teardown
@@ -281,6 +279,26 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal copy, source
end
+ def test_caching_image_path_with_caching_and_proc_asset_host_using_request
+ ENV['RAILS_ASSET_ID'] = ''
+ ActionController::Base.asset_host = Proc.new do |source, request|
+ if request.ssl?
+ "#{request.protocol}#{request.host_with_port}"
+ else
+ "#{request.protocol}assets#{source.length}.example.com"
+ end
+ end
+
+ ActionController::Base.perform_caching = true
+
+
+ @controller.request.stubs(:ssl?).returns(false)
+ assert_equal "http://assets15.example.com/images/xml.png", image_path("xml.png")
+
+ @controller.request.stubs(:ssl?).returns(true)
+ assert_equal "http://localhost/images/xml.png", image_path("xml.png")
+ end
+
def test_caching_javascript_include_tag_when_caching_on
ENV["RAILS_ASSET_ID"] = ""
ActionController::Base.asset_host = 'http://a0.example.com'
diff --git a/actionpack/test/template/benchmark_helper_test.rb b/actionpack/test/template/benchmark_helper_test.rb
index 08d453c965..5d2af7cdd9 100644
--- a/actionpack/test/template/benchmark_helper_test.rb
+++ b/actionpack/test/template/benchmark_helper_test.rb
@@ -4,32 +4,25 @@ require 'action_view/helpers/benchmark_helper'
class BenchmarkHelperTest < ActionView::TestCase
tests ActionView::Helpers::BenchmarkHelper
- class MockLogger
- attr_reader :logged
-
- def initialize
- @logged = []
- end
-
- def method_missing(method, *args)
- @logged << [method, args]
- end
+ def teardown
+ controller.logger.send(:clear_buffer)
end
def controller
- @controller ||= Struct.new(:logger).new(MockLogger.new)
+ logger = ActiveSupport::BufferedLogger.new(StringIO.new)
+ logger.auto_flushing = false
+ @controller ||= Struct.new(:logger).new(logger)
end
def test_without_block
assert_raise(LocalJumpError) { benchmark }
- assert controller.logger.logged.empty?
+ assert buffer.empty?
end
def test_defaults
i_was_run = false
benchmark { i_was_run = true }
assert i_was_run
- assert 1, controller.logger.logged.size
assert_last_logged
end
@@ -37,24 +30,57 @@ class BenchmarkHelperTest < ActionView::TestCase
i_was_run = false
benchmark('test_run') { i_was_run = true }
assert i_was_run
- assert 1, controller.logger.logged.size
assert_last_logged 'test_run'
end
- def test_with_message_and_level
+ def test_with_message_and_deprecated_level
i_was_run = false
- benchmark('debug_run', :debug) { i_was_run = true }
+
+ assert_deprecated do
+ benchmark('debug_run', :debug) { i_was_run = true }
+ end
+
assert i_was_run
- assert 1, controller.logger.logged.size
- assert_last_logged 'debug_run', :debug
+ assert_last_logged 'debug_run'
+ end
+
+ def test_within_level
+ controller.logger.level = ActiveSupport::BufferedLogger::DEBUG
+ benchmark('included_debug_run', :level => :debug) { }
+ assert_last_logged 'included_debug_run'
+ end
+
+ def test_outside_level
+ controller.logger.level = ActiveSupport::BufferedLogger::ERROR
+ benchmark('skipped_debug_run', :level => :debug) { }
+ assert_no_match(/skipped_debug_run/, buffer.last)
+ ensure
+ controller.logger.level = ActiveSupport::BufferedLogger::DEBUG
end
+ def test_without_silencing
+ benchmark('debug_run', :silence => false) do
+ controller.logger.info "not silenced!"
+ end
+
+ assert_equal 2, buffer.size
+ end
+
+ def test_with_silencing
+ benchmark('debug_run', :silence => true) do
+ controller.logger.info "silenced!"
+ end
+
+ assert_equal 1, buffer.size
+ end
+
+
private
- def assert_last_logged(message = 'Benchmarking', level = :info)
- last = controller.logger.logged.last
- assert 2, last.size
- assert_equal level, last.first
- assert 1, last[1].size
- assert last[1][0] =~ /^#{message} \(.*\)$/
+ def buffer
+ controller.logger.send(:buffer)
+ end
+
+ def assert_last_logged(message = 'Benchmarking')
+ assert_match(/^#{message} \(.*\)$/, buffer.last)
end
end
diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb
index a68b09bb45..caea1bd643 100644
--- a/actionpack/test/template/compiled_templates_test.rb
+++ b/actionpack/test/template/compiled_templates_test.rb
@@ -31,7 +31,7 @@ uses_mocha 'TestTemplateRecompilation' do
end
def test_compiled_template_will_always_be_recompiled_when_template_is_not_cached
- ActionView::Template.any_instance.expects(:loaded?).times(3).returns(false)
+ ActionView::Template.any_instance.expects(:recompile?).times(3).returns(true)
assert_equal 0, @compiled_templates.instance_methods.size
assert_equal "Hello world!", render(:file => "#{FIXTURE_LOAD_PATH}/test/hello_world.erb")
ActionView::Template.any_instance.expects(:compile!).times(3)
@@ -62,13 +62,14 @@ uses_mocha 'TestTemplateRecompilation' do
def render_with_cache(*args)
view_paths = ActionController::Base.view_paths
- assert view_paths.first.loaded?
+ assert_equal ActionView::Template::EagerPath, view_paths.first.class
ActionView::Base.new(view_paths, {}).render(*args)
end
def render_without_cache(*args)
- view_paths = ActionView::Base.process_view_paths(FIXTURE_LOAD_PATH)
- assert !view_paths.first.loaded?
+ path = ActionView::Template::Path.new(FIXTURE_LOAD_PATH)
+ view_paths = ActionView::Base.process_view_paths(path)
+ assert_equal ActionView::Template::Path, view_paths.first.class
ActionView::Base.new(view_paths, {}).render(*args)
end
diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb
index dc9616db3b..fac30da128 100644
--- a/actionpack/test/template/date_helper_i18n_test.rb
+++ b/actionpack/test/template/date_helper_i18n_test.rb
@@ -78,6 +78,8 @@ class DateHelperSelectTagsI18nTests < Test::Unit::TestCase
uses_mocha 'date_helper_select_tags_i18n_tests' do
def setup
+ @prompt_defaults = {:year => 'Year', :month => 'Month', :day => 'Day', :hour => 'Hour', :minute => 'Minute', :second => 'Seconds'}
+
I18n.stubs(:translate).with(:'date.month_names', :locale => 'en').returns Date::MONTHNAMES
end
@@ -98,6 +100,15 @@ class DateHelperSelectTagsI18nTests < Test::Unit::TestCase
select_month(8, :locale => 'en', :use_short_month => true)
end
+ def test_date_or_time_select_translates_prompts
+ @prompt_defaults.each do |key, prompt|
+ I18n.expects(:translate).with(('datetime.prompts.' + key.to_s).to_sym, :locale => 'en').returns prompt
+ end
+
+ I18n.expects(:translate).with(:'date.order', :locale => 'en').returns [:year, :month, :day]
+ datetime_select('post', 'updated_at', :locale => 'en', :include_seconds => true, :prompt => true)
+ end
+
# date_or_time_select
def test_date_or_time_select_given_an_order_options_does_not_translate_order
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index 49ba140c23..6ec01b7a8f 100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -153,6 +153,22 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_day(16, {}, :class => 'selector')
end
+ def test_select_day_with_default_prompt
+ expected = %(<select id="date_day" name="date[day]">\n)
+ expected << %(<option value="">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"
+
+ assert_dom_equal expected, select_day(16, :prompt => true)
+ end
+
+ def test_select_day_with_custom_prompt
+ expected = %(<select id="date_day" name="date[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"
+
+ assert_dom_equal expected, select_day(16, :prompt => 'Choose day')
+ end
+
def test_select_month
expected = %(<select id="date_month" name="date[month]">\n)
expected << %(<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)
@@ -276,6 +292,22 @@ class DateHelperTest < ActionView::TestCase
#assert result.include?('<option value="1">January')
end
+ def test_select_month_with_default_prompt
+ expected = %(<select id="date_month" name="date[month]">\n)
+ expected << %(<option value="">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"
+
+ assert_dom_equal expected, select_month(8, :prompt => true)
+ end
+
+ def test_select_month_with_custom_prompt
+ expected = %(<select id="date_month" name="date[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"
+
+ assert_dom_equal expected, select_month(8, :prompt => 'Choose month')
+ end
+
def test_select_year
expected = %(<select id="date_year" name="date[year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
@@ -344,6 +376,22 @@ class DateHelperTest < ActionView::TestCase
#assert result.include?('<option value="2003"')
end
+ def test_select_year_with_default_prompt
+ expected = %(<select id="date_year" name="date[year]">\n)
+ expected << %(<option value="">Year</option>\n<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_year(nil, :start_year => 2003, :end_year => 2005, :prompt => true)
+ end
+
+ def test_select_year_with_custom_prompt
+ expected = %(<select id="date_year" name="date[year]">\n)
+ expected << %(<option value="">Choose year</option>\n<option value="2003">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_year(nil, :start_year => 2003, :end_year => 2005, :prompt => 'Choose year')
+ end
+
def test_select_hour
expected = %(<select id="date_hour" name="date[hour]">\n)
expected << %(<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">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<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)
@@ -392,6 +440,22 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), {}, :class => 'selector', :accesskey => 'M')
end
+ def test_select_hour_with_default_prompt
+ expected = %(<select id="date_hour" name="date[hour]">\n)
+ expected << %(<option value="">Hour</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">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<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)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), :prompt => true)
+ end
+
+ def test_select_hour_with_custom_prompt
+ expected = %(<select id="date_hour" name="date[hour]">\n)
+ expected << %(<option value="">Choose hour</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">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<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)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), :prompt => 'Choose hour')
+ end
+
def test_select_minute
expected = %(<select id="date_minute" name="date[minute]">\n)
expected << %(<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)
@@ -470,6 +534,22 @@ class DateHelperTest < ActionView::TestCase
#assert result.include?('<option value="00">00')
end
+ def test_select_minute_with_default_prompt
+ expected = %(<select id="date_minute" name="date[minute]">\n)
+ expected << %(<option value="">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_minute(Time.mktime(2003, 8, 16, 8, 4, 18), :prompt => true)
+ end
+
+ def test_select_minute_with_custom_prompt
+ expected = %(<select id="date_minute" name="date[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_minute(Time.mktime(2003, 8, 16, 8, 4, 18), :prompt => 'Choose minute')
+ end
+
def test_select_second
expected = %(<select id="date_second" name="date[second]">\n)
expected << %(<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">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" selected="selected">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)
@@ -524,6 +604,22 @@ class DateHelperTest < ActionView::TestCase
#assert result.include?('<option value="00">00')
end
+ def test_select_second_with_default_prompt
+ expected = %(<select id="date_second" name="date[second]">\n)
+ expected << %(<option value="">Seconds</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">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" selected="selected">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_second(Time.mktime(2003, 8, 16, 8, 4, 18), :prompt => true)
+ end
+
+ def test_select_second_with_custom_prompt
+ expected = %(<select id="date_second" name="date[second]">\n)
+ expected << %(<option value="">Choose seconds</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">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" selected="selected">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_second(Time.mktime(2003, 8, 16, 8, 4, 18), :prompt => 'Choose seconds')
+ end
+
def test_select_date
expected = %(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
@@ -914,6 +1010,57 @@ class DateHelperTest < ActionView::TestCase
assert_nothing_raised { select_datetime(Date.today) }
end
+ def test_select_datetime_with_default_prompt
+ expected = %(<select id="date_first_year" name="date[first][year]">\n)
+ expected << %(<option value="">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="">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="">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 << %(<select id="date_first_hour" name="date[first][hour]">\n)
+ expected << %(<option value="">Hour</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">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<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)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_first_minute" name="date[first][minute]">\n)
+ expected << %(<option value="">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,
+ :prefix => "date[first]", :prompt => true)
+ end
+
+ def test_select_datetime_with_custom_prompt
+
+ 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 << %(<select id="date_first_hour" name="date[first][hour]">\n)
+ expected << %(<option value="">Choose hour</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">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<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)
+ expected << "</select>\n"
+
+ 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, :prefix => "date[first]",
+ :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year', :hour => 'Choose hour', :minute => 'Choose minute'})
+ end
+
def test_select_time
expected = %(<select id="date_hour" name="date[hour]">\n)
expected << %(<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">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<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)
@@ -995,6 +1142,40 @@ class DateHelperTest < ActionView::TestCase
assert_nothing_raised { select_time(Date.today) }
end
+ def test_select_time_with_default_prompt
+ expected = %(<select id="date_hour" name="date[hour]">\n)
+ expected << %(<option value="">Hour</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">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<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)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_minute" name="date[minute]">\n)
+ expected << %(<option value="">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"
+
+ expected << %(<select id="date_second" name="date[second]">\n)
+ expected << %(<option value="">Seconds</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">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" selected="selected">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_time(Time.mktime(2003, 8, 16, 8, 4, 18), :include_seconds => true, :prompt => true)
+ end
+
+ def test_select_time_with_custom_prompt
+
+ expected = %(<select id="date_hour" name="date[hour]">\n)
+ expected << %(<option value="">Choose hour</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">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<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)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_minute" name="date[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"
+
+ expected << %(<select id="date_second" name="date[second]">\n)
+ expected << %(<option value="">Choose seconds</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">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" selected="selected">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_time(Time.mktime(2003, 8, 16, 8, 4, 18), :prompt => true, :include_seconds => true,
+ :prompt => {:hour => 'Choose hour', :minute => 'Choose minute', :second => 'Choose seconds'})
+ end
+
def test_date_select
@post = Post.new
@post.written_on = Date.new(2004, 6, 15)
@@ -1277,6 +1458,46 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, date_select("post", "written_on", { :date_separator => " / " })
end
+ def test_date_select_with_default_prompt
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+ expected << %{<option value="">Year</option>\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+ expected << %{<option value="">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" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">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="post_written_on_3i" name="post[written_on(3i)]">\n}
+ expected << %{<option value="">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" selected="selected">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}
+
+ expected << "</select>\n"
+
+ assert_dom_equal expected, date_select("post", "written_on", :prompt => true)
+ end
+
+ def test_date_select_with_custom_prompt
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+ expected << %{<option value="">Choose year</option>\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\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" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">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="post_written_on_3i" name="post[written_on(3i)]">\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" selected="selected">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}
+
+ expected << "</select>\n"
+
+ assert_dom_equal expected, date_select("post", "written_on", :prompt => {:year => 'Choose year', :month => 'Choose month', :day => 'Choose day'})
+ end
+
def test_time_select
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
@@ -1403,6 +1624,48 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, time_select("post", "written_on", { :time_separator => " - ", :include_seconds => true })
end
+ def test_time_select_with_default_prompt
+ @post = Post.new
+ @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
+ expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
+ expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
+
+ expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
+ expected << %(<option value="">Hour</option>\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 << " : "
+ expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n)
+ expected << %(<option value="">Minute</option>\n)
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ assert_dom_equal expected, time_select("post", "written_on", :prompt => true)
+ end
+
+ def test_time_select_with_custom_prompt
+ @post = Post.new
+ @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
+ expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
+ expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
+
+ expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
+ expected << %(<option value="">Choose hour</option>\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 << " : "
+ expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n)
+ expected << %(<option value="">Choose minute</option>\n)
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ assert_dom_equal expected, time_select("post", "written_on", :prompt => {:hour => 'Choose hour', :minute => 'Choose minute'})
+ end
+
def test_datetime_select
@post = Post.new
@post.updated_at = Time.local(2004, 6, 15, 16, 35)
@@ -1526,6 +1789,64 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, datetime_select("post", "updated_at", { :date_separator => " / ", :datetime_separator => " , ", :time_separator => " - ", :include_seconds => true })
end
+ def test_datetime_select_with_default_prompt
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+
+ expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+ expected << %{<option value="">Year</option>\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+ expected << %{<option value="">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" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">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="post_updated_at_3i" name="post[updated_at(3i)]">\n}
+ expected << %{<option value="">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" selected="selected">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}
+ expected << "</select>\n"
+
+ expected << " &mdash; "
+
+ expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+ expected << %{<option value="">Hour</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">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" 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}
+ expected << "</select>\n"
+ expected << " : "
+ expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\n}
+ expected << %{<option value="">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">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" selected="selected">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, datetime_select("post", "updated_at", :prompt => true)
+ end
+
+ def test_datetime_select_with_custom_prompt
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+
+ expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+ expected << %{<option value="">Choose year</option>\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\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" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">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="post_updated_at_3i" name="post[updated_at(3i)]">\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" selected="selected">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}
+ expected << "</select>\n"
+
+ expected << " &mdash; "
+
+ expected << %{<select id="post_updated_at_4i" name="post[updated_at(4i)]">\n}
+ expected << %{<option value="">Choose hour</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">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" 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}
+ expected << "</select>\n"
+ expected << " : "
+ expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\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">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" selected="selected">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, datetime_select("post", "updated_at", :prompt => {:year => 'Choose year', :month => 'Choose month', :day => 'Choose day', :hour => 'Choose hour', :minute => 'Choose minute'})
+ end
+
def test_date_select_with_zero_value_and_no_start_year
expected = %(<select id="date_first_year" name="date[first][year]">\n)
(Date.today.year-5).upto(Date.today.year+1) { |y| expected << %(<option value="#{y}">#{y}</option>\n) }
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 52e8bf376a..9454fd7e91 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -463,7 +463,7 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
- def test_nested_fields_for_with_index
+ def test_nested_fields_for_with_index_and_parent_fields
form_for('post', @post, :index => 1) do |c|
concat c.text_field(:title)
c.fields_for('comment', @comment, :index => 1) do |r|
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 0387a11de2..4bd897efeb 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -197,7 +197,7 @@ class CachedViewRenderTest < Test::Unit::TestCase
# Ensure view path cache is primed
def setup
view_paths = ActionController::Base.view_paths
- assert view_paths.first.loaded?
+ assert_equal ActionView::Template::EagerPath, view_paths.first.class
setup_view(view_paths)
end
end
@@ -208,8 +208,9 @@ class LazyViewRenderTest < Test::Unit::TestCase
# Test the same thing as above, but make sure the view path
# is not eager loaded
def setup
- view_paths = ActionView::Base.process_view_paths(FIXTURE_LOAD_PATH)
- assert !view_paths.first.loaded?
+ path = ActionView::Template::Path.new(FIXTURE_LOAD_PATH)
+ view_paths = ActionView::Base.process_view_paths(path)
+ assert_equal ActionView::Template::Path, view_paths.first.class
setup_view(view_paths)
end
end
diff --git a/actionpack/test/view/test_case_test.rb b/actionpack/test/view/test_case_test.rb
new file mode 100644
index 0000000000..9124198b28
--- /dev/null
+++ b/actionpack/test/view/test_case_test.rb
@@ -0,0 +1,8 @@
+require 'abstract_unit'
+
+class TestCaseTest < ActionView::TestCase
+ def test_should_have_current_url
+ controller = TestController.new
+ assert_nothing_raised(NoMethodError){ controller.url_for({:controller => "foo", :action => "index"}) }
+ end
+end
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 423a0bd0cc..c750f486f9 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,8 +1,12 @@
*2.3.0/3.0*
+* Added dynamic scopes ala dynamic finders #1648 [Yaroslav Markin]
+
+* Fixed that ActiveRecord::Base#new_record? should return false (not nil) for existing records #1219 [Yaroslav Markin]
+
* I18n the word separator for error messages. Introduces the activerecord.errors.format.separator translation key. #1294 [Akira Matsuda]
-* Add :having as a key to find and the relevant associations. [miloops]
+* Add :having as a key to find and the relevant associations. [Emilio Tagua]
* Added default_scope to Base #1381 [Paweł Kondzior]. Example:
@@ -29,19 +33,19 @@
* Skip collection ids reader optimization if using :finder_sql [Jeremy Kemper]
-* Add Model#delete instance method, similar to Model.delete class method. #1086 [Hongli Lai]
+* Add Model#delete instance method, similar to Model.delete class method. #1086 [Hongli Lai (Phusion)]
* MySQL: cope with quirky default values for not-null text columns. #1043 [Frederick Cheung]
* Multiparameter attributes skip time zone conversion for time-only columns [#1030 state:resolved] [Geoff Buesing]
-* Base.skip_time_zone_conversion_for_attributes uses class_inheritable_accessor, so that subclasses don't overwrite Base [#346 state:resolved] [miloops]
+* Base.skip_time_zone_conversion_for_attributes uses class_inheritable_accessor, so that subclasses don't overwrite Base [#346 state:resolved] [Emilio Tagua]
-* Added find_last_by dynamic finder #762 [miloops]
+* Added find_last_by dynamic finder #762 [Emilio Tagua]
-* Internal API: configurable association options and build_association method for reflections so plugins may extend and override. #985 [Hongli Lai]
+* Internal API: configurable association options and build_association method for reflections so plugins may extend and override. #985 [Hongli Lai (Phusion)]
-* Changed benchmarks to be reported in milliseconds [DHH]
+* Changed benchmarks to be reported in milliseconds [David Heinemeier Hansson]
* Connection pooling. #936 [Nick Sieger]
@@ -90,7 +94,7 @@
*2.1.0 (May 31st, 2008)*
-* Add ActiveRecord::Base.sti_name that checks ActiveRecord::Base#store_full_sti_class? and returns either the full or demodulized name. [rick]
+* Add ActiveRecord::Base.sti_name that checks ActiveRecord::Base#store_full_sti_class? and returns either the full or demodulized name. [Rick Olson]
* Add first/last methods to associations/named_scope. Resolved #226. [Ryan Bates]
@@ -123,27 +127,27 @@
* Fixed that change_column should be able to use :null => true on a field that formerly had false [Nate Wiger] [#26]
-* Added that the MySQL adapter should map integer to either smallint, int, or bigint depending on the :limit just like PostgreSQL [DHH]
+* Added that the MySQL adapter should map integer to either smallint, int, or bigint depending on the :limit just like PostgreSQL [David Heinemeier Hansson]
-* Change validates_uniqueness_of :case_sensitive option default back to true (from [9160]). Love your database columns, don't LOWER them. [rick]
+* Change validates_uniqueness_of :case_sensitive option default back to true (from [9160]). Love your database columns, don't LOWER them. [Rick Olson]
-* Add support for interleaving migrations by storing which migrations have run in the new schema_migrations table. Closes #11493 [jordi]
+* Add support for interleaving migrations by storing which migrations have run in the new schema_migrations table. Closes #11493 [Jordi Bunster]
-* ActiveRecord::Base#sum defaults to 0 if no rows are returned. Closes #11550 [kamal]
+* ActiveRecord::Base#sum defaults to 0 if no rows are returned. Closes #11550 [Kamal Fariz Mahyuddin]
-* Ensure that respond_to? considers dynamic finder methods. Closes #11538. [floehopper]
+* Ensure that respond_to? considers dynamic finder methods. Closes #11538. [James Mead]
-* Ensure that save on parent object fails for invalid has_one association. Closes #10518. [Pratik]
+* Ensure that save on parent object fails for invalid has_one association. Closes #10518. [Pratik Naik]
-* Remove duplicate code from associations. [Pratik]
+* Remove duplicate code from associations. [Pratik Naik]
-* Refactor HasManyThroughAssociation to inherit from HasManyAssociation. Association callbacks and <association>_ids= now work with hm:t. #11516 [rubyruy]
+* Refactor HasManyThroughAssociation to inherit from HasManyAssociation. Association callbacks and <association>_ids= now work with hm:t. #11516 [Ruy Asan]
-* Ensure HABTM#create and HABTM#build do not load entire association. [Pratik]
+* Ensure HABTM#create and HABTM#build do not load entire association. [Pratik Naik]
* Improve documentation. [Xavier Noria, Jack Danger Canty, leethal]
-* Tweak ActiveRecord::Base#to_json to include a root value in the returned hash: {"post": {"title": ...}} [rick]
+* Tweak ActiveRecord::Base#to_json to include a root value in the returned hash: {"post": {"title": ...}} [Rick Olson]
Post.find(1).to_json # => {"title": ...}
config.active_record.include_root_in_json = true
@@ -151,9 +155,9 @@
* Add efficient #include? to AssociationCollection (for has_many/has_many :through/habtm). [stopdropandrew]
-* PostgreSQL: create_ and drop_database support. #9042 [ez, pedz, nicksieger]
+* PostgreSQL: create_ and drop_database support. #9042 [ez, pedz, Nick Sieger]
-* Ensure that validates_uniqueness_of works with with_scope. Closes #9235. [nik.wakelin, cavalle]
+* Ensure that validates_uniqueness_of works with with_scope. Closes #9235. [Nik Wakelin, cavalle]
* Partial updates include only unsaved attributes. Off by default; set YourClass.partial_updates = true to enable. [Jeremy Kemper]
@@ -163,21 +167,21 @@
* Track changes to unsaved attributes. [Jeremy Kemper]
-* Switched to UTC-timebased version numbers for migrations and the schema. This will as good as eliminate the problem of multiple migrations getting the same version assigned in different branches. Also added rake db:migrate:up/down to apply individual migrations that may need to be run when you merge branches #11458 [jbarnette]
+* Switched to UTC-timebased version numbers for migrations and the schema. This will as good as eliminate the problem of multiple migrations getting the same version assigned in different branches. Also added rake db:migrate:up/down to apply individual migrations that may need to be run when you merge branches #11458 [John Barnette]
-* Fixed that has_many :through would ignore the hash conditions #11447 [miloops]
+* Fixed that has_many :through would ignore the hash conditions #11447 [Emilio Tagua]
* Fix issue where the :uniq option of a has_many :through association is ignored when find(:all) is called. Closes #9407 [cavalle]
* Fix duplicate table alias error when including an association with a has_many :through association on the same join table. Closes #7310 [cavalle]
-* More efficient association preloading code that compacts a through_records array in a central location. Closes #11427 [danger]
+* More efficient association preloading code that compacts a through_records array in a central location. Closes #11427 [Jack Danger Canty]
-* Improve documentation. [Radar, Jan De Poorter, chuyeow, xaviershay, danger, miloops, Xavier Noria, Sunny Ripert]
+* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert]
-* Fixed that ActiveRecord#Base.find_or_create/initialize would not honor attr_protected/accessible when used with a hash #11422 [miloops]
+* Fixed that ActiveRecord#Base.find_or_create/initialize would not honor attr_protected/accessible when used with a hash #11422 [Emilio Tagua]
-* Added ActiveRecord#Base.all/first/last as aliases for find(:all/:first/:last) #11413 [nkallen, thechrisoshow]
+* Added ActiveRecord#Base.all/first/last as aliases for find(:all/:first/:last) #11413 [nkallen, Chris O'Sullivan]
* Merge the has_finder gem, renamed as 'named_scope'. #11404 [nkallen]
@@ -193,25 +197,25 @@
See http://pivots.pivotallabs.com/users/nick/blog/articles/284-hasfinder-it-s-now-easier-than-ever-to-create-complex-re-usable-sql-queries
-* Add has_one :through support. #4756 [thechrisoshow]
+* Add has_one :through support. #4756 [Chris O'Sullivan]
-* Migrations: create_table supports primary_key_prefix_type. #10314 [student, thechrisoshow]
+* Migrations: create_table supports primary_key_prefix_type. #10314 [student, Chris O'Sullivan]
* Added logging for dependency load errors with fixtures #11056 [stuthulhu]
* Time zone aware attributes use Time#in_time_zone [Geoff Buesing]
-* Fixed that scoped joins would not always be respected #6821 [Theory/Danger]
+* Fixed that scoped joins would not always be respected #6821 [Theory/Jack Danger Canty]
* Ensure that ActiveRecord::Calculations disambiguates field names with the table name. #11027 [cavalle]
* Added add/remove_timestamps to the schema statements for adding the created_at/updated_at columns on existing tables #11129 [jramirez]
-* Added ActiveRecord::Base.find(:last) #11338 [miloops]
+* Added ActiveRecord::Base.find(:last) #11338 [Emilio Tagua]
* test_native_types expects DateTime.local_offset instead of DateTime.now.offset; fixes test breakage due to dst transition [Geoff Buesing]
-* Add :readonly option to HasManyThrough associations. #11156 [miloops]
+* Add :readonly option to HasManyThrough associations. #11156 [Emilio Tagua]
* Improve performance on :include/:conditions/:limit queries by selectively joining in the pre-query. #9560 [dasil003]
@@ -231,7 +235,7 @@
* Optimise the BigDecimal conversion code. #11110 [adymo]
-* Introduce the :readonly option to all associations. Records from the association cannot be saved. #11084 [miloops]
+* Introduce the :readonly option to all associations. Records from the association cannot be saved. #11084 [Emilio Tagua]
* Multiparameter attributes for time columns fail over to DateTime when out of range of Time [Geoff Buesing]
@@ -239,15 +243,15 @@
* Add timezone-aware attribute readers and writers. #10982 [Geoff Buesing]
-* Instantiating time objects in multiparameter attributes uses Time.zone if available. #10982 [rick]
+* Instantiating time objects in multiparameter attributes uses Time.zone if available. #10982 [Rick Olson]
-* Add note about how ActiveRecord::Observer classes are initialized in a Rails app. #10980 [fxn]
+* Add note about how ActiveRecord::Observer classes are initialized in a Rails app. #10980 [Xavier Noria]
* MySQL: omit text/blob defaults from the schema instead of using an empty string. #10963 [mdeiters]
* belongs_to supports :dependent => :destroy and :delete. #10592 [Jonathan Viney]
-* Introduce preload query strategy for eager :includes. #9640 [Frederick Cheung, Aleksey Kondratenko, codafoo]
+* Introduce preload query strategy for eager :includes. #9640 [Frederick Cheung, Aliaksey Kandratsenka, codafoo]
* Support aggregations in finder conditions. #10572 [Ryan Kinderman]
@@ -263,13 +267,13 @@
* update_all ignores scoped :order and :limit, so post.comments.update_all doesn't try to include the comment order in the update statement. #10686 [Brendan Ribera]
-* Added ActiveRecord::Base.cache_key to make it easier to cache Active Records in combination with the new ActiveSupport::Cache::* libraries [DHH]
+* Added ActiveRecord::Base.cache_key to make it easier to cache Active Records in combination with the new ActiveSupport::Cache::* libraries [David Heinemeier Hansson]
* Make sure CSV fixtures are compatible with ruby 1.9's new csv implementation. [JEG2]
* Added by parameter to increment, decrement, and their bang varieties so you can do player1.increment!(:points, 5) #10542 [Sam]
-* Optimize ActiveRecord::Base#exists? to use #select_all instead of #find. Closes #10605 [jamesh, fcheung, protocool]
+* Optimize ActiveRecord::Base#exists? to use #select_all instead of #find. Closes #10605 [jamesh, Frederick Cheung, protocool]
* Don't unnecessarily load has_many associations in after_update callbacks. Closes #6822 [stopdropandrew, canadaduane]
@@ -282,11 +286,11 @@
*2.0.2* (December 16th, 2007)
-* Ensure optimistic locking handles nil #lock_version values properly. Closes #10510 [rick]
+* Ensure optimistic locking handles nil #lock_version values properly. Closes #10510 [Rick Olson]
* Make the Fixtures Test::Unit enhancements more supporting for double-loaded test cases. Closes #10379 [brynary]
-* Fix that validates_acceptance_of still works for non-existent tables (useful for bootstrapping new databases). Closes #10474 [hasmanyjosh]
+* Fix that validates_acceptance_of still works for non-existent tables (useful for bootstrapping new databases). Closes #10474 [Josh Susser]
* Ensure that the :uniq option for has_many :through associations retains the order. #10463 [remvee]
@@ -297,19 +301,19 @@
*2.0.1* (December 7th, 2007)
-* Removed query cache rescue as it could cause code to be run twice (closes #10408) [DHH]
+* Removed query cache rescue as it could cause code to be run twice (closes #10408) [David Heinemeier Hansson]
*2.0.0* (December 6th, 2007)
* Anchor DateTimeTest to fixed DateTime instead of a variable value based on Time.now#advance#to_datetime, so that this test passes on 64-bit platforms running Ruby 1.8.6+ [Geoff Buesing]
-* Fixed that the Query Cache should just be ignored if the database is misconfigured (so that the "About your applications environment" works even before the database has been created) [DHH]
+* Fixed that the Query Cache should just be ignored if the database is misconfigured (so that the "About your applications environment" works even before the database has been created) [David Heinemeier Hansson]
* Fixed that the truncation of strings longer than 50 chars should use inspect
so newlines etc are escaped #10385 [Norbert Crombach]
-* Fixed that habtm associations should be able to set :select as part of their definition and have that honored [DHH]
+* Fixed that habtm associations should be able to set :select as part of their definition and have that honored [David Heinemeier Hansson]
* Document how the :include option can be used in Calculations::calculate. Closes #7446 [adamwiggins, ultimoamore]
@@ -319,7 +323,7 @@ so newlines etc are escaped #10385 [Norbert Crombach]
* More complete documentation for find_by_sql. Closes #7912 [fearoffish]
-* Added ActiveRecord::Base#becomes to turn a record into one of another class (mostly relevant for STIs) [DHH]. Example:
+* Added ActiveRecord::Base#becomes to turn a record into one of another class (mostly relevant for STIs) [David Heinemeier Hansson]. Example:
render :partial => @client.becomes(Company) # renders companies/company instead of clients/client
@@ -345,25 +349,25 @@ so newlines etc are escaped #10385 [Norbert Crombach]
* attr_readonly behaves well with optimistic locking. #10188 [Nick Bugajski]
-* Base#to_xml supports the nil="true" attribute like Hash#to_xml. #8268 [Catfish]
+* Base#to_xml supports the nil="true" attribute like Hash#to_xml. #8268 [Jonathan del Strother]
-* Change plings to the more conventional quotes in the documentation. Closes #10104 [danger]
+* Change plings to the more conventional quotes in the documentation. Closes #10104 [Jack Danger Canty]
-* Fix HasManyThrough Association so it uses :conditions on the HasMany Association. Closes #9729 [danger]
+* Fix HasManyThrough Association so it uses :conditions on the HasMany Association. Closes #9729 [Jack Danger Canty]
* Ensure that column names are quoted. Closes #10134 [wesley.moxam]
-* Smattering of grammatical fixes to documentation. Closes #10083 [BobSilva]
+* Smattering of grammatical fixes to documentation. Closes #10083 [Bob Silva]
-* Enhance explanation with more examples for attr_accessible macro. Closes #8095 [fearoffish, Marcel Molina]
+* Enhance explanation with more examples for attr_accessible macro. Closes #8095 [fearoffish, Marcel Molina Jr.]
* Update association/method mapping table to refected latest collection methods for has_many :through. Closes #8772 [Pratik Naik]
-* Explain semantics of having several different AR instances in a transaction block. Closes #9036 [jacobat, Marcel Molina]
+* Explain semantics of having several different AR instances in a transaction block. Closes #9036 [jacobat, Marcel Molina Jr.]
-* Update Schema documentation to use updated sexy migration notation. Closes #10086 [sjgman9]
+* Update Schema documentation to use updated sexy migration notation. Closes #10086 [Sam Granieri]
-* Make fixtures work with the new test subclasses. [Tarmo Tänav, Koz]
+* Make fixtures work with the new test subclasses. [Tarmo Tänav, Michael Koziarski]
* Introduce finder :joins with associations. Same :include syntax but with inner rather than outer joins. #10012 [RubyRedRick]
# Find users with an avatar
@@ -374,7 +378,7 @@ so newlines etc are escaped #10385 [Norbert Crombach]
* Associations: speedup duplicate record check. #10011 [Pratik Naik]
-* Make sure that << works on has_many associations on unsaved records. Closes #9989 [hasmanyjosh]
+* Make sure that << works on has_many associations on unsaved records. Closes #9989 [Josh Susser]
* Allow association redefinition in subclasses. #9346 [wildchild]
@@ -397,7 +401,7 @@ so newlines etc are escaped #10385 [Norbert Crombach]
* Use VALID_FIND_OPTIONS when resolving :find scoping rather than hard coding the list of valid find options. Closes #9443 [sur]
-* Limited eager loading no longer ignores scoped :order. Closes #9561 [danger, Josh Peek]
+* Limited eager loading no longer ignores scoped :order. Closes #9561 [Jack Danger Canty, Josh Peek]
* Assigning an instance of a foreign class to a composed_of aggregate calls an optional conversion block. Refactor and simplify composed_of implementation. #6322 [brandon, Chris Cruft]
@@ -410,7 +414,7 @@ so newlines etc are escaped #10385 [Norbert Crombach]
* Complete the assimilation of Sexy Migrations from ErrFree [Chris Wanstrath, PJ Hyett]
http://errtheblog.com/post/2381
-* Qualified column names work in hash conditions, like :conditions => { 'comments.created_at' => ... }. #9733 [danger]
+* Qualified column names work in hash conditions, like :conditions => { 'comments.created_at' => ... }. #9733 [Jack Danger Canty]
* Fix regression where the association would not construct new finder SQL on save causing bogus queries for "WHERE owner_id = NULL" even after owner was saved. #8713 [Bryan Helmkamp]
@@ -420,11 +424,11 @@ so newlines etc are escaped #10385 [Norbert Crombach]
* Alias association #build to #new so it behaves predictably. #8787 [Pratik Naik]
-* Add notes to documentation regarding attr_readonly behavior with counter caches and polymorphic associations. Closes #9835 [saimonmoore, rick]
+* Add notes to documentation regarding attr_readonly behavior with counter caches and polymorphic associations. Closes #9835 [saimonmoore, Rick Olson]
* Observers can observe model names as symbols properly now. Closes #9869 [queso]
-* find_and_(initialize|create)_by methods can now properly initialize protected attributes [Tobias Luetke]
+* find_and_(initialize|create)_by methods can now properly initialize protected attributes [Tobias Lütke]
* belongs_to infers the foreign key from the association name instead of from the class name. [Jeremy Kemper]
@@ -443,16 +447,16 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
* Allow change_column to set NOT NULL in the PostgreSQL adapter [Tarmo Tänav]
-* Fix that ActiveRecord would create attribute methods and override custom attribute getters if the method is also defined in Kernel.methods. [Rick]
+* Fix that ActiveRecord would create attribute methods and override custom attribute getters if the method is also defined in Kernel.methods. [Rick Olson]
-* Don't call attr_readonly on polymorphic belongs_to associations, in case it matches the name of some other non-ActiveRecord class/module. [Rick]
+* Don't call attr_readonly on polymorphic belongs_to associations, in case it matches the name of some other non-ActiveRecord class/module. [Rick Olson]
* Try loading activerecord-<adaptername>-adapter gem before trying a plain require so you can use custom gems for the bundled adapters. Also stops gems from requiring an adapter from an old Active Record gem. [Jeremy Kemper, Derrick Spell]
*2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.14.2 - 1.15.3]
-* Add attr_readonly to specify columns that are skipped during a normal ActiveRecord #save operation. Closes #6896 [dcmanges]
+* Add attr_readonly to specify columns that are skipped during a normal ActiveRecord #save operation. Closes #6896 [Dan Manges]
class Comment < ActiveRecord::Base
# Automatically sets Article#comments_count as readonly.
@@ -463,7 +467,7 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
attr_readonly :approved_comments_count
end
-* Make size for has_many :through use counter cache if it exists. Closes #9734 [xaviershay]
+* Make size for has_many :through use counter cache if it exists. Closes #9734 [Xavier Shay]
* Remove DB2 adapter since IBM chooses to maintain their own adapter instead. [Jeremy Kemper]
@@ -479,9 +483,9 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
* Added the possibility of using symbols in addition to concrete classes with ActiveRecord::Observer#observe. #3998 [Robby Russell, Tarmo Tänav]
-* Added ActiveRecord::Base#to_json/from_json [DHH, Cheah Chu Yeow]
+* Added ActiveRecord::Base#to_json/from_json [David Heinemeier Hansson, Cheah Chu Yeow]
-* Added ActiveRecord::Base#from_xml [DHH]. Example:
+* Added ActiveRecord::Base#from_xml [David Heinemeier Hansson]. Example:
xml = "<person><name>David</name></person>"
Person.new.from_xml(xml).name # => "David"
@@ -516,27 +520,27 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
* Explicitly require active_record/query_cache before using it. [Jeremy Kemper]
-* Fix bug where unserializing an attribute attempts to modify a frozen @attributes hash for a deleted record. [Rick, marclove]
+* Fix bug where unserializing an attribute attempts to modify a frozen @attributes hash for a deleted record. [Rick Olson, marclove]
* Performance: absorb instantiate and initialize_with_callbacks into the Base methods. [Jeremy Kemper]
-* Fixed that eager loading queries and with_scope should respect the :group option [DHH]
+* Fixed that eager loading queries and with_scope should respect the :group option [David Heinemeier Hansson]
* Improve performance and functionality of the postgresql adapter. Closes #8049 [roderickvd]
For more information see: http://dev.rubyonrails.org/ticket/8049
-* Don't clobber includes passed to has_many.count [danger]
+* Don't clobber includes passed to has_many.count [Jack Danger Canty]
-* Make sure has_many uses :include when counting [danger]
+* Make sure has_many uses :include when counting [Jack Danger Canty]
-* Change the implementation of ActiveRecord's attribute reader and writer methods [nzkoz]
+* Change the implementation of ActiveRecord's attribute reader and writer methods [Michael Koziarski]
- Generate Reader and Writer methods which cache attribute values in hashes. This is to avoid repeatedly parsing the same date or integer columns.
- Change exception raised when users use find with :select then try to access a skipped column. Plugins could override missing_attribute() to lazily load the columns.
- Move method definition to the class, instead of the instance
- Always generate the readers, writers and predicate methods.
-* Perform a deep #dup on query cache results so that modifying activerecord attributes does not modify the cached attributes. [Rick]
+* Perform a deep #dup on query cache results so that modifying activerecord attributes does not modify the cached attributes. [Rick Olson]
# Ensure that has_many :through associations use a count query instead of loading the target when #size is called. Closes #8800 [Pratik Naik]
@@ -556,7 +560,7 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
* Remove deprecated count(conditions=nil, joins=nil) usage. Closes #8993 [Pratik Naik]
-* Change belongs_to so that the foreign_key assumption is taken from the association name, not the class name. Closes #8992 [hasmanyjosh]
+* Change belongs_to so that the foreign_key assumption is taken from the association name, not the class name. Closes #8992 [Josh Susser]
OLD
belongs_to :visitor, :class_name => 'User' # => inferred foreign_key is user_id
@@ -570,7 +574,7 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
* Move from select * to select tablename.* to avoid clobbering IDs. Closes #8889 [dasil003]
-* Don't call unsupported methods on associated objects when using :include, :method with to_xml #7307, [manfred, jwilger]
+* Don't call unsupported methods on associated objects when using :include, :method with to_xml #7307, [Manfred Stienstra, jwilger]
* Define collection singular ids method for has_many :through associations. #8763 [Pratik Naik]
@@ -584,7 +588,7 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
* Update tests' use of fixtures for the new collections api. #8726 [Kamal Fariz Mahyuddin]
-* Save associated records only if the association is already loaded. #8713 [blaine]
+* Save associated records only if the association is already loaded. #8713 [Blaine]
* MySQL: fix show_variable. #8448 [matt, Jeremy Kemper]
@@ -628,7 +632,7 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
* Load database adapters on demand. Eliminates config.connection_adapters and RAILS_CONNECTION_ADAPTERS. Add your lib directory to the $LOAD_PATH and put your custom adapter in lib/active_record/connection_adapters/adaptername_adapter.rb. This way you can provide custom adapters as plugins or gems without modifying Rails. [Jeremy Kemper]
-* Ensure that associations with :dependent => :delete_all respect :conditions option. Closes #8034 [danger, Josh Peek, Rick]
+* Ensure that associations with :dependent => :delete_all respect :conditions option. Closes #8034 [Jack Danger Canty, Josh Peek, Rick Olson]
* belongs_to assignment creates a new proxy rather than modifying its target in-place. #8412 [mmangino@elevatedrails.com]
@@ -640,13 +644,13 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
* Sanitize Base#inspect. #8392, #8623 [Nik Wakelin, jnoon]
-* Replace the transaction {|transaction|..} semantics with a new Exception ActiveRecord::Rollback. [Koz]
+* Replace the transaction {|transaction|..} semantics with a new Exception ActiveRecord::Rollback. [Michael Koziarski]
* Oracle: extract column length for CHAR also. #7866 [ymendel]
* Document :allow_nil option for validates_acceptance_of since it defaults to true. [tzaharia]
-* Update documentation for :dependent declaration so that it explicitly uses the non-deprecated API. [danger]
+* Update documentation for :dependent declaration so that it explicitly uses the non-deprecated API. [Jack Danger Canty]
* Add documentation caveat about when to use count_by_sql. [fearoffish]
@@ -656,7 +660,7 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
* Add documentation for :encoding option to mysql adapter. [marclove]
-* Added short-hand declaration style to migrations (inspiration from Sexy Migrations, http://errtheblog.com/post/2381) [DHH]. Example:
+* Added short-hand declaration style to migrations (inspiration from Sexy Migrations, http://errtheblog.com/post/2381) [David Heinemeier Hansson]. Example:
create_table "products" do |t|
t.column "shop_id", :integer
@@ -675,17 +679,17 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
t.timestamps
end
-* Use association name for the wrapper element when using .to_xml. Previous behavior lead to non-deterministic situations with STI and polymorphic associations. [Koz, jstrachan]
+* Use association name for the wrapper element when using .to_xml. Previous behavior lead to non-deterministic situations with STI and polymorphic associations. [Michael Koziarski, jstrachan]
* Improve performance of calling .create on has_many :through associations. [evan]
* Improved cloning performance by relying less on exception raising #8159 [Blaine]
-* Added ActiveRecord::Base.inspect to return a column-view like #<Post id:integer, title:string, body:text> [DHH]
+* Added ActiveRecord::Base.inspect to return a column-view like #<Post id:integer, title:string, body:text> [David Heinemeier Hansson]
-* Added yielding of Builder instance for ActiveRecord::Base#to_xml calls [DHH]
+* Added yielding of Builder instance for ActiveRecord::Base#to_xml calls [David Heinemeier Hansson]
-* Small additions and fixes for ActiveRecord documentation. Closes #7342 [jeremymcanally]
+* Small additions and fixes for ActiveRecord documentation. Closes #7342 [Jeremy McAnally]
* Add helpful debugging info to the ActiveRecord::StatementInvalid exception in ActiveRecord::ConnectionAdapters::SqliteAdapter#table_structure. Closes #7925. [court3nay]
@@ -695,13 +699,13 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
* Base.update_all :order and :limit options. Useful for MySQL updates that must be ordered to avoid violating unique constraints. [Jeremy Kemper]
-* Remove deprecated object transactions. People relying on this functionality should install the object_transactions plugin at http://code.bitsweat.net/svn/object_transactions. Closes #5637 [Koz, Jeremy Kemper]
+* Remove deprecated object transactions. People relying on this functionality should install the object_transactions plugin at http://code.bitsweat.net/svn/object_transactions. Closes #5637 [Michael Koziarski, Jeremy Kemper]
* PostgreSQL: remove DateTime -> Time downcast. Warning: do not enable translate_results for the C bindings if you have timestamps outside Time's domain. [Jeremy Kemper]
* find_or_create_by_* takes a hash so you can create with more attributes than are in the method name. For example, Person.find_or_create_by_name(:name => 'Henry', :comments => 'Hi new user!') is equivalent to Person.find_by_name('Henry') || Person.create(:name => 'Henry', :comments => 'Hi new user!'). #7368 [Josh Susser]
-* Make sure with_scope takes both :select and :joins into account when setting :readonly. Allows you to save records you retrieve using method_missing on a has_many :through associations. [Koz]
+* Make sure with_scope takes both :select and :joins into account when setting :readonly. Allows you to save records you retrieve using method_missing on a has_many :through associations. [Michael Koziarski]
* Allow a polymorphic :source for has_many :through associations. Closes #7143 [protocool]
@@ -726,13 +730,13 @@ single-table inheritance. #3833, #9886 [Gabriel Gironda, rramdas, François Bea
* Test DateTime native type in migrations, including an edge case with dates
during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
-* SQLServer: correctly schema-dump tables with no indexes or descending indexes. #7333, #7703 [Jakob S, Tom Ward]
+* SQLServer: correctly schema-dump tables with no indexes or descending indexes. #7333, #7703 [Jakob Skjerning, Tom Ward]
* SQLServer: recognize real column type as Ruby float. #7057 [sethladd, Tom Ward]
-* Added fixtures :all as a way of loading all fixtures in the fixture directory at once #7214 [manfred]
+* Added fixtures :all as a way of loading all fixtures in the fixture directory at once #7214 [Manfred Stienstra]
-* Added database connection as a yield parameter to ActiveRecord::Base.transaction so you can manually rollback [DHH]. Example:
+* Added database connection as a yield parameter to ActiveRecord::Base.transaction so you can manually rollback [David Heinemeier Hansson]. Example:
transaction do |transaction|
david.withdrawal(100)
@@ -742,14 +746,14 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Made increment_counter/decrement_counter play nicely with optimistic locking, and added a more general update_counters method [Jamis Buck]
-* Reworked David's query cache to be available as Model.cache {...}. For the duration of the block no select query should be run more then once. Any inserts/deletes/executes will flush the whole cache however [Tobias Luetke]
+* Reworked David's query cache to be available as Model.cache {...}. For the duration of the block no select query should be run more then once. Any inserts/deletes/executes will flush the whole cache however [Tobias Lütke]
Task.cache { Task.find(1); Task.find(1) } #=> 1 query
* When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. [Jamis Buck]
* Oracle: fix lob and text default handling. #7344 [gfriedrich, Michael Schoen]
-* SQLServer: don't choke on strings containing 'null'. #7083 [Jakob S]
+* SQLServer: don't choke on strings containing 'null'. #7083 [Jakob Skjerning]
* MySQL: blob and text columns may not have defaults in 5.x. Update fixtures schema for strict mode. #6695 [Dan Kubb]
@@ -759,7 +763,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Use Date#to_s(:db) for quoted dates. #7411 [Michael Schoen]
-* Don't create instance writer methods for class attributes. Closes #7401 [Rick]
+* Don't create instance writer methods for class attributes. Closes #7401 [Rick Olson]
* Docs: validations examples. #7343 [zackchandler]
@@ -779,11 +783,11 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Make sure sqlite3 driver closes open connections on disconnect [Rob Rasmussen]
-* [DOC] clear up some ambiguity with the way has_and_belongs_to_many creates the default join table name. #7072 [jeremymcanally]
+* [DOC] clear up some ambiguity with the way has_and_belongs_to_many creates the default join table name. #7072 [Jeremy McAnally]
* change_column accepts :default => nil. Skip column options for primary keys. #6956, #7048 [Dan Manges, Jeremy Kemper]
-* MySQL, PostgreSQL: change_column_default quotes the default value and doesn't lose column type information. #3987, #6664 [Jonathan Viney, manfred, altano@bigfoot.com]
+* MySQL, PostgreSQL: change_column_default quotes the default value and doesn't lose column type information. #3987, #6664 [Jonathan Viney, Manfred Stienstra, altano@bigfoot.com]
* Oracle: create_table takes a :sequence_name option to override the 'tablename_seq' default. #7000 [Michael Schoen]
@@ -818,15 +822,15 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* fix faulty inheritance tests and that eager loading grabs the wrong inheritance column when the class of your association is an STI subclass. Closes #6859 [protocool]
-* Consolidated different create and create! versions to call through to the base class with scope. This fixes inconsistencies, especially related to protected attribtues. Closes #5847 [Alexander Dymo, Tobias Luetke]
+* Consolidated different create and create! versions to call through to the base class with scope. This fixes inconsistencies, especially related to protected attribtues. Closes #5847 [Alexander Dymo, Tobias Lütke]
* find supports :lock with :include. Check whether your database allows SELECT ... FOR UPDATE with outer joins before using. #6764 [vitaly, Jeremy Kemper]
* Add AssociationCollection#create! to be consistent with AssociationCollection#create when dealing with a foreign key that is a protected attribute [Cody Fauser]
-* Added counter optimization for AssociationCollection#any? so person.friends.any? won't actually load the full association if we have the count in a cheaper form [DHH]
+* Added counter optimization for AssociationCollection#any? so person.friends.any? won't actually load the full association if we have the count in a cheaper form [David Heinemeier Hansson]
-* Change fixture_path to a class inheritable accessor allowing test cases to have their own custom set of fixtures. #6672 [zdennis]
+* Change fixture_path to a class inheritable accessor allowing test cases to have their own custom set of fixtures. #6672 [Zach Dennis]
* Quote ActiveSupport::Multibyte::Chars. #6653 [Julian Tarkhanov]
@@ -881,15 +885,15 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Fix has_many :through to add the appropriate conditions when going through an association using STI. Closes #5783. [Jonathan Viney]
-* fix select_limited_ids_list issues in postgresql, retain current behavior in other adapters [Rick]
+* fix select_limited_ids_list issues in postgresql, retain current behavior in other adapters [Rick Olson]
-* Restore eager condition interpolation, document it's differences [Rick]
+* Restore eager condition interpolation, document it's differences [Rick Olson]
* Don't rollback in teardown unless a transaction was started. Don't start a transaction in create_fixtures if a transaction is started. #6282 [Jacob Fugal, Jeremy Kemper]
* Add #delete support to has_many :through associations. Closes #6049 [Martin Landers]
-* Reverted old select_limited_ids_list postgresql fix that caused issues in mysql. Closes #5851 [Rick]
+* Reverted old select_limited_ids_list postgresql fix that caused issues in mysql. Closes #5851 [Rick Olson]
* Removes the ability for eager loaded conditions to be interpolated, since there is no model instance to use as a context for interpolation. #5553 [turnip@turnipspatch.com]
@@ -897,7 +901,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Added update_attributes! which uses save! to raise an exception if a validation error prevents saving #6192 [jonathan]
-* Deprecated add_on_boundary_breaking (use validates_length_of instead) #6292 [BobSilva]
+* Deprecated add_on_boundary_breaking (use validates_length_of instead) #6292 [Bob Silva]
* The has_many create method works with polymorphic associations. #6361 [Dan Peterson]
@@ -913,9 +917,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* has_one associations with a nil target may be safely marshaled. #6279 [norbauer, Jeremy Kemper]
-* Duplicate the hash provided to AR::Base#to_xml to prevent unexpected side effects [Koz]
+* Duplicate the hash provided to AR::Base#to_xml to prevent unexpected side effects [Michael Koziarski]
-* Add a :namespace option to AR::Base#to_xml [Koz]
+* Add a :namespace option to AR::Base#to_xml [Michael Koziarski]
* Deprecation tests. Remove warnings for dynamic finders and for the foo_count method if it's also an attribute. [Jeremy Kemper]
@@ -923,7 +927,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Improve yaml fixtures error reporting. #6205 [Bruce Williams]
-* Rename AR::Base#quote so people can use that name in their models. #3628 [Koz]
+* Rename AR::Base#quote so people can use that name in their models. #3628 [Michael Koziarski]
* Add deprecation warning for inferred foreign key. #6029 [Josh Susser]
@@ -937,7 +941,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* to_xml: the :methods option works on arrays of records. #5845 [Josh Starcher]
-* Deprecation: update docs. #5998 [jakob@mentalized.net, Kevin Clark]
+* Deprecation: update docs. #5998 [Jakob Skjerning, Kevin Clark]
* Add some XmlSerialization tests for ActiveRecord [Rick Olson]
@@ -963,9 +967,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* MySQL: update test schema for MySQL 5 strict mode. #5861 [Tom Ward]
-* to_xml: correct naming of included associations. #5831 [josh.starcher@gmail.com]
+* to_xml: correct naming of included associations. #5831 [Josh Starcher]
-* Pushing a record onto a has_many :through sets the association's foreign key to the associate's primary key and adds it to the correct association. #5815, #5829 [josh@hasmanythrough.com]
+* Pushing a record onto a has_many :through sets the association's foreign key to the associate's primary key and adds it to the correct association. #5815, #5829 [Josh Susser]
* Add records to has_many :through using <<, push, and concat by creating the association record. Raise if base or associate are new records since both ids are required to create the association. #build raises since you can't associate an unsaved record. #create! takes an attributes hash and creates the associated record and its association in a transaction. [Jeremy Kemper]
@@ -981,7 +985,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Cache nil results for :included has_one associations also. #5787 [Michael Schoen]
-* Fixed a bug which would cause .save to fail after trying to access a empty has_one association on a unsaved record. [Tobias Luetke]
+* Fixed a bug which would cause .save to fail after trying to access a empty has_one association on a unsaved record. [Tobias Lütke]
* Nested classes are given table names prefixed by the singular form of the parent's table name. [Jeremy Kemper]
Example: Invoice::Lineitem is given table name invoice_lineitems
@@ -1011,7 +1015,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
-* Cache nil results for has_one associations so multiple calls don't call the database. Closes #5757. [Michael A. Schoen]
+* Cache nil results for has_one associations so multiple calls don't call the database. Closes #5757. [Michael Schoen]
* Add documentation for how to disable timestamps on a per model basis. Closes #5684. [matt@mattmargolis.net Marcel Molina Jr.]
@@ -1019,9 +1023,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Refactor ActiveRecord::Base.reset_subclasses to #reset, and add global observer resetting. [Rick Olson]
-* Formally deprecate the deprecated finders. [Koz]
+* Formally deprecate the deprecated finders. [Michael Koziarski]
-* Formally deprecate rich associations. [Koz]
+* Formally deprecate rich associations. [Michael Koziarski]
* Fixed that default timezones for new / initialize should uphold utc setting #5709 [daniluk@yahoo.com]
@@ -1055,7 +1059,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* SQLServer: fix db:schema:dump case-sensitivity. #4684 [Will Rogers]
-* Oracle: BigDecimal support. #5667 [schoenm@earthlink.net]
+* Oracle: BigDecimal support. #5667 [Michael Schoen]
* Numeric and decimal columns map to BigDecimal instead of Float. Those with scale 0 map to Integer. #5454 [robbat2@gentoo.org, work@ashleymoran.me.uk]
@@ -1071,9 +1075,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Clearer has_one/belongs_to model names (account has_one :user). #5632 [matt@mattmargolis.net]
-* Oracle: use nonblocking queries if allow_concurrency is set, fix pessimistic locking, don't guess date vs. time by default (set OracleAdapter.emulate_dates = true for the old behavior), adapter cleanup. #5635 [schoenm@earthlink.net]
+* Oracle: use nonblocking queries if allow_concurrency is set, fix pessimistic locking, don't guess date vs. time by default (set OracleAdapter.emulate_dates = true for the old behavior), adapter cleanup. #5635 [Michael Schoen]
-* Fixed a few Oracle issues: Allows Oracle's odd date handling to still work consistently within #to_xml, Passes test that hardcode insert statement by dropping the :id column, Updated RUNNING_UNIT_TESTS with Oracle instructions, Corrects method signature for #exec #5294 [schoenm@earthlink.net]
+* Fixed a few Oracle issues: Allows Oracle's odd date handling to still work consistently within #to_xml, Passes test that hardcode insert statement by dropping the :id column, Updated RUNNING_UNIT_TESTS with Oracle instructions, Corrects method signature for #exec #5294 [Michael Schoen]
* Added :group to available options for finds done on associations #5516 [mike@michaeldewey.org]
@@ -1108,9 +1112,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Use explicit delegation instead of method aliasing for AR::Base.to_param -> AR::Base.id. #5299 (skaes@web.de)
-* Refactored ActiveRecord::Base.to_xml to become a delegate for XmlSerializer, which restores sanity to the mega method. This refactoring also reinstates the opinions that type="string" is redundant and ugly and nil-differentiation is not a concern of serialization [DHH]
+* Refactored ActiveRecord::Base.to_xml to become a delegate for XmlSerializer, which restores sanity to the mega method. This refactoring also reinstates the opinions that type="string" is redundant and ugly and nil-differentiation is not a concern of serialization [David Heinemeier Hansson]
-* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [hcatlin@gmail.com]. Example:
+* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [Hampton Catlin]. Example:
Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2)
@@ -1141,26 +1145,26 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
Foo.find(:all, :conditions => ['bar_id IN (?)', bars])
Foo.find(:first, :conditions => ['bar_id = ?', bar])
-* Fixed that Base.find :all, :conditions => [ "id IN (?)", collection ] would fail if collection was empty [DHH]
+* Fixed that Base.find :all, :conditions => [ "id IN (?)", collection ] would fail if collection was empty [David Heinemeier Hansson]
-* Add a list of regexes assert_queries skips in the ActiveRecord test suite. [Rick]
+* Add a list of regexes assert_queries skips in the ActiveRecord test suite. [Rick Olson]
-* Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 [josh@hasmanythrough.com]
+* Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 [Josh Susser]
* Provide Association Extensions access to the instance that the association is being accessed from.
- Closes #4433 [josh@hasmanythrough.com]
+ Closes #4433 [Josh Susser]
* Update OpenBase adaterp's maintainer's email address. Closes #5176. [Derrick Spell]
-* Add a quick note about :select and eagerly included associations. [Rick]
+* Add a quick note about :select and eagerly included associations. [Rick Olson]
* Add docs for the :as option in has_one associations. Closes #5144 [cdcarter@gmail.com]
-* Fixed that has_many collections shouldn't load the entire association to do build or create [DHH]
+* Fixed that has_many collections shouldn't load the entire association to do build or create [David Heinemeier Hansson]
-* Added :allow_nil option for aggregations #5091 [ian.w.white@gmail.com]
+* Added :allow_nil option for aggregations #5091 [Ian White]
-* Fix Oracle boolean support and tests. Closes #5139. [schoenm@earthlink.net]
+* Fix Oracle boolean support and tests. Closes #5139. [Michael Schoen]
* create! no longer blows up when no attributes are passed and a :create scope is in effect (e.g. foo.bars.create! failed whereas foo.bars.create!({}) didn't.) [Jeremy Kemper]
@@ -1198,7 +1202,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Add warning about the proper way to validate the presence of a foreign key. Closes #4147. [Francois Beausoleil <francois.beausoleil@gmail.com>]
-* Fix syntax error in documentation. Closes #4679. [mislav@nippur.irb.hr]
+* Fix syntax error in documentation. Closes #4679. [Mislav Marohnić]
* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu]
@@ -1212,14 +1216,14 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Sybase Adapter type conversion cleanup. Closes #4736. [dev@metacasa.net]
-* Fix bug where calculations with long alias names return null. [Rick]
+* Fix bug where calculations with long alias names return null. [Rick Olson]
-* Raise error when trying to add to a has_many :through association. Use the Join Model instead. [Rick]
+* Raise error when trying to add to a has_many :through association. Use the Join Model instead. [Rick Olson]
@post.tags << @tag # BAD
@post.taggings.create(:tag => @tag) # GOOD
-* Allow all calculations to take the :include option, not just COUNT (closes #4840) [Rick]
+* Allow all calculations to take the :include option, not just COUNT (closes #4840) [Rick Olson]
* Update inconsistent migrations documentation. #4683 [machomagna@gmail.com]
@@ -1227,17 +1231,17 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Properly quote index names in migrations (closes #4764) [John Long]
-* Fix the HasManyAssociation#count method so it uses the new ActiveRecord::Base#count syntax, while maintaining backwards compatibility. [Rick]
+* Fix the HasManyAssociation#count method so it uses the new ActiveRecord::Base#count syntax, while maintaining backwards compatibility. [Rick Olson]
-* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick]
+* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick Olson]
-* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick]
+* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick Olson]
* DRY up association collection reader method generation. [Marcel Molina Jr.]
* DRY up and tweak style of the validation error object. [Marcel Molina Jr.]
-* Add :case_sensitive option to validates_uniqueness_of (closes #3090) [Rick]
+* Add :case_sensitive option to validates_uniqueness_of (closes #3090) [Rick Olson]
class Account < ActiveRecord::Base
validates_uniqueness_of :email, :case_sensitive => false
@@ -1262,14 +1266,14 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
*1.15.2* (February 5th, 2007)
-* Pass a range in :conditions to use the SQL BETWEEN operator. #6974 [dcmanges]
+* Pass a range in :conditions to use the SQL BETWEEN operator. #6974 [Dan Manges]
Student.find(:all, :conditions => { :grade => 9..12 })
-* Don't create instance writer methods for class attributes. [Rick]
+* Don't create instance writer methods for class attributes. [Rick Olson]
* When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. [Jamis Buck]
-* SQLServer: don't choke on strings containing 'null'. #7083 [Jakob S]
+* SQLServer: don't choke on strings containing 'null'. #7083 [Jakob Skjerning]
* Consistently use LOWER() for uniqueness validations (rather than mixing with UPPER()) so the database can always use a functional index on the lowercased column. #6495 [Si]
@@ -1287,11 +1291,11 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
*1.15.0* (January 16th, 2007)
-* [DOC] clear up some ambiguity with the way has_and_belongs_to_many creates the default join table name. #7072 [jeremymcanally]
+* [DOC] clear up some ambiguity with the way has_and_belongs_to_many creates the default join table name. #7072 [Jeremy McAnally]
-* change_column accepts :default => nil. Skip column options for primary keys. #6956, #7048 [dcmanges, Jeremy Kemper]
+* change_column accepts :default => nil. Skip column options for primary keys. #6956, #7048 [Dan Manges, Jeremy Kemper]
-* MySQL, PostgreSQL: change_column_default quotes the default value and doesn't lose column type information. #3987, #6664 [Jonathan Viney, manfred, altano@bigfoot.com]
+* MySQL, PostgreSQL: change_column_default quotes the default value and doesn't lose column type information. #3987, #6664 [Jonathan Viney, Manfred Stienstra, altano@bigfoot.com]
* Oracle: create_table takes a :sequence_name option to override the 'tablename_seq' default. #7000 [Michael Schoen]
@@ -1352,15 +1356,15 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Fix has_many :through to add the appropriate conditions when going through an association using STI. Closes #5783. [Jonathan Viney]
-* fix select_limited_ids_list issues in postgresql, retain current behavior in other adapters [Rick]
+* fix select_limited_ids_list issues in postgresql, retain current behavior in other adapters [Rick Olson]
-* Restore eager condition interpolation, document it's differences [Rick]
+* Restore eager condition interpolation, document it's differences [Rick Olson]
* Don't rollback in teardown unless a transaction was started. Don't start a transaction in create_fixtures if a transaction is started. #6282 [Jacob Fugal, Jeremy Kemper]
* Add #delete support to has_many :through associations. Closes #6049 [Martin Landers]
-* Reverted old select_limited_ids_list postgresql fix that caused issues in mysql. Closes #5851 [Rick]
+* Reverted old select_limited_ids_list postgresql fix that caused issues in mysql. Closes #5851 [Rick Olson]
* Removes the ability for eager loaded conditions to be interpolated, since there is no model instance to use as a context for interpolation. #5553 [turnip@turnipspatch.com]
@@ -1368,7 +1372,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Added update_attributes! which uses save! to raise an exception if a validation error prevents saving #6192 [jonathan]
-* Deprecated add_on_boundary_breaking (use validates_length_of instead) #6292 [BobSilva]
+* Deprecated add_on_boundary_breaking (use validates_length_of instead) #6292 [Bob Silva]
* The has_many create method works with polymorphic associations. #6361 [Dan Peterson]
@@ -1384,9 +1388,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* has_one associations with a nil target may be safely marshaled. #6279 [norbauer, Jeremy Kemper]
-* Duplicate the hash provided to AR::Base#to_xml to prevent unexpected side effects [Koz]
+* Duplicate the hash provided to AR::Base#to_xml to prevent unexpected side effects [Michael Koziarski]
-* Add a :namespace option to AR::Base#to_xml [Koz]
+* Add a :namespace option to AR::Base#to_xml [Michael Koziarski]
* Deprecation tests. Remove warnings for dynamic finders and for the foo_count method if it's also an attribute. [Jeremy Kemper]
@@ -1394,7 +1398,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Improve yaml fixtures error reporting. #6205 [Bruce Williams]
-* Rename AR::Base#quote so people can use that name in their models. #3628 [Koz]
+* Rename AR::Base#quote so people can use that name in their models. #3628 [Michael Koziarski]
* Add deprecation warning for inferred foreign key. #6029 [Josh Susser]
@@ -1424,9 +1428,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* MySQL: update test schema for MySQL 5 strict mode. #5861 [Tom Ward]
-* to_xml: correct naming of included associations. #5831 [josh.starcher@gmail.com]
+* to_xml: correct naming of included associations. #5831 [Josh Starcher]
-* Pushing a record onto a has_many :through sets the association's foreign key to the associate's primary key and adds it to the correct association. #5815, #5829 [josh@hasmanythrough.com]
+* Pushing a record onto a has_many :through sets the association's foreign key to the associate's primary key and adds it to the correct association. #5815, #5829 [Josh Susser]
* Add records to has_many :through using <<, push, and concat by creating the association record. Raise if base or associate are new records since both ids are required to create the association. #build raises since you can't associate an unsaved record. #create! takes an attributes hash and creates the associated record and its association in a transaction. [Jeremy Kemper]
@@ -1442,7 +1446,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Cache nil results for :included has_one associations also. #5787 [Michael Schoen]
-* Fixed a bug which would cause .save to fail after trying to access a empty has_one association on a unsaved record. [Tobias Luetke]
+* Fixed a bug which would cause .save to fail after trying to access a empty has_one association on a unsaved record. [Tobias Lütke]
* Nested classes are given table names prefixed by the singular form of the parent's table name. [Jeremy Kemper]
Example: Invoice::Lineitem is given table name invoice_lineitems
@@ -1470,15 +1474,15 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
-* Cache nil results for has_one associations so multiple calls don't call the database. Closes #5757. [Michael A. Schoen]
+* Cache nil results for has_one associations so multiple calls don't call the database. Closes #5757. [Michael Schoen]
* Don't save has_one associations unnecessarily. #5735 [Jonathan Viney]
* Refactor ActiveRecord::Base.reset_subclasses to #reset, and add global observer resetting. [Rick Olson]
-* Formally deprecate the deprecated finders. [Koz]
+* Formally deprecate the deprecated finders. [Michael Koziarski]
-* Formally deprecate rich associations. [Koz]
+* Formally deprecate rich associations. [Michael Koziarski]
* Fixed that default timezones for new / initialize should uphold utc setting #5709 [daniluk@yahoo.com]
@@ -1512,7 +1516,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* SQLServer: fix db:schema:dump case-sensitivity. #4684 [Will Rogers]
-* Oracle: BigDecimal support. #5667 [schoenm@earthlink.net]
+* Oracle: BigDecimal support. #5667 [Michael Schoen]
* Numeric and decimal columns map to BigDecimal instead of Float. Those with scale 0 map to Integer. #5454 [robbat2@gentoo.org, work@ashleymoran.me.uk]
@@ -1526,9 +1530,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Clearer has_one/belongs_to model names (account has_one :user). #5632 [matt@mattmargolis.net]
-* Oracle: use nonblocking queries if allow_concurrency is set, fix pessimistic locking, don't guess date vs. time by default (set OracleAdapter.emulate_dates = true for the old behavior), adapter cleanup. #5635 [schoenm@earthlink.net]
+* Oracle: use nonblocking queries if allow_concurrency is set, fix pessimistic locking, don't guess date vs. time by default (set OracleAdapter.emulate_dates = true for the old behavior), adapter cleanup. #5635 [Michael Schoen]
-* Fixed a few Oracle issues: Allows Oracle's odd date handling to still work consistently within #to_xml, Passes test that hardcode insert statement by dropping the :id column, Updated RUNNING_UNIT_TESTS with Oracle instructions, Corrects method signature for #exec #5294 [schoenm@earthlink.net]
+* Fixed a few Oracle issues: Allows Oracle's odd date handling to still work consistently within #to_xml, Passes test that hardcode insert statement by dropping the :id column, Updated RUNNING_UNIT_TESTS with Oracle instructions, Corrects method signature for #exec #5294 [Michael Schoen]
* Added :group to available options for finds done on associations #5516 [mike@michaeldewey.org]
@@ -1561,9 +1565,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Use explicit delegation instead of method aliasing for AR::Base.to_param -> AR::Base.id. #5299 (skaes@web.de)
-* Refactored ActiveRecord::Base.to_xml to become a delegate for XmlSerializer, which restores sanity to the mega method. This refactoring also reinstates the opinions that type="string" is redundant and ugly and nil-differentiation is not a concern of serialization [DHH]
+* Refactored ActiveRecord::Base.to_xml to become a delegate for XmlSerializer, which restores sanity to the mega method. This refactoring also reinstates the opinions that type="string" is redundant and ugly and nil-differentiation is not a concern of serialization [David Heinemeier Hansson]
-* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [hcatlin@gmail.com]. Example:
+* Added simple hash conditions to find that'll just convert hash to an AND-based condition string #5143 [Hampton Catlin]. Example:
Person.find(:all, :conditions => { :last_name => "Catlin", :status => 1 }, :limit => 2)
@@ -1594,26 +1598,26 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
Foo.find(:all, :conditions => ['bar_id IN (?)', bars])
Foo.find(:first, :conditions => ['bar_id = ?', bar])
-* Fixed that Base.find :all, :conditions => [ "id IN (?)", collection ] would fail if collection was empty [DHH]
+* Fixed that Base.find :all, :conditions => [ "id IN (?)", collection ] would fail if collection was empty [David Heinemeier Hansson]
-* Add a list of regexes assert_queries skips in the ActiveRecord test suite. [Rick]
+* Add a list of regexes assert_queries skips in the ActiveRecord test suite. [Rick Olson]
-* Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 [josh@hasmanythrough.com]
+* Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 [Josh Susser]
* Provide Association Extensions access to the instance that the association is being accessed from.
- Closes #4433 [josh@hasmanythrough.com]
+ Closes #4433 [Josh Susser]
* Update OpenBase adaterp's maintainer's email address. Closes #5176. [Derrick Spell]
-* Add a quick note about :select and eagerly included associations. [Rick]
+* Add a quick note about :select and eagerly included associations. [Rick Olson]
* Add docs for the :as option in has_one associations. Closes #5144 [cdcarter@gmail.com]
-* Fixed that has_many collections shouldn't load the entire association to do build or create [DHH]
+* Fixed that has_many collections shouldn't load the entire association to do build or create [David Heinemeier Hansson]
-* Added :allow_nil option for aggregations #5091 [ian.w.white@gmail.com]
+* Added :allow_nil option for aggregations #5091 [Ian White]
-* Fix Oracle boolean support and tests. Closes #5139. [schoenm@earthlink.net]
+* Fix Oracle boolean support and tests. Closes #5139. [Michael Schoen]
* create! no longer blows up when no attributes are passed and a :create scope is in effect (e.g. foo.bars.create! failed whereas foo.bars.create!({}) didn't.) [Jeremy Kemper]
@@ -1639,7 +1643,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Add warning about the proper way to validate the presence of a foreign key. Closes #4147. [Francois Beausoleil <francois.beausoleil@gmail.com>]
-* Fix syntax error in documentation. Closes #4679. [mislav@nippur.irb.hr]
+* Fix syntax error in documentation. Closes #4679. [Mislav Marohnić]
* Add Oracle support for CLOB inserts. Closes #4748. [schoenm@earthlink.net sandra.metz@duke.edu]
@@ -1653,26 +1657,26 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Sybase Adapter type conversion cleanup. Closes #4736. [dev@metacasa.net]
-* Fix bug where calculations with long alias names return null. [Rick]
+* Fix bug where calculations with long alias names return null. [Rick Olson]
-* Raise error when trying to add to a has_many :through association. Use the Join Model instead. [Rick]
+* Raise error when trying to add to a has_many :through association. Use the Join Model instead. [Rick Olson]
@post.tags << @tag # BAD
@post.taggings.create(:tag => @tag) # GOOD
-* Allow all calculations to take the :include option, not just COUNT (closes #4840) [Rick]
+* Allow all calculations to take the :include option, not just COUNT (closes #4840) [Rick Olson]
* Add ActiveRecord::Errors#to_xml [Jamis Buck]
* Properly quote index names in migrations (closes #4764) [John Long]
-* Fix the HasManyAssociation#count method so it uses the new ActiveRecord::Base#count syntax, while maintaining backwards compatibility. [Rick]
+* Fix the HasManyAssociation#count method so it uses the new ActiveRecord::Base#count syntax, while maintaining backwards compatibility. [Rick Olson]
-* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick]
+* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick Olson]
-* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick]
+* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick Olson]
-* Add :case_sensitive option to validates_uniqueness_of (closes #3090) [Rick]
+* Add :case_sensitive option to validates_uniqueness_of (closes #3090) [Rick Olson]
class Account < ActiveRecord::Base
validates_uniqueness_of :email, :case_sensitive => false
@@ -1689,7 +1693,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Add warning about the proper way to validate the presence of a foreign key. #4147 [Francois Beausoleil <francois.beausoleil@gmail.com>]
-* Fix syntax error in documentation. #4679 [mislav@nippur.irb.hr]
+* Fix syntax error in documentation. #4679 [Mislav Marohnić]
* Update inconsistent migrations documentation. #4683 [machomagna@gmail.com]
@@ -1702,9 +1706,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Properly quote index names in migrations (closes #4764) [John Long]
-* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick]
+* Ensure that Associations#include_eager_conditions? checks both scoped and explicit conditions [Rick Olson]
-* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick]
+* Associations#select_limited_ids_list adds the ORDER BY columns to the SELECT DISTINCT List for postgresql. [Rick Olson]
*1.14.2* (April 9th, 2006)
@@ -1718,17 +1722,17 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Fixed that that multiparameter assignment doesn't work with aggregations (closes #4620) [Lars Pind]
-* Enable Limit/Offset in Calculations (closes #4558) [lmarlow@yahoo.com]
+* Enable Limit/Offset in Calculations (closes #4558) [lmarlow]
-* Fixed that loading including associations returns all results if Load IDs For Limited Eager Loading returns none (closes #4528) [Rick]
+* Fixed that loading including associations returns all results if Load IDs For Limited Eager Loading returns none (closes #4528) [Rick Olson]
* Fixed HasManyAssociation#find bugs when :finder_sql is set #4600 [lagroue@free.fr]
-* Allow AR::Base#respond_to? to behave when @attributes is nil [zenspider]
+* Allow AR::Base#respond_to? to behave when @attributes is nil [Ryan Davis]
-* Support eager includes when going through a polymorphic has_many association. [Rick]
+* Support eager includes when going through a polymorphic has_many association. [Rick Olson]
-* Added support for eagerly including polymorphic has_one associations. (closes #4525) [Rick]
+* Added support for eagerly including polymorphic has_one associations. (closes #4525) [Rick Olson]
class Post < ActiveRecord::Base
has_one :tagging, :as => :taggable
@@ -1736,9 +1740,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
Post.find :all, :include => :tagging
-* Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many [Rick]
+* Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many [Rick Olson]
-* Added support for going through a polymorphic has_many association: (closes #4401) [Rick]
+* Added support for going through a polymorphic has_many association: (closes #4401) [Rick Olson]
class PhotoCollection < ActiveRecord::Base
has_many :photos, :as => :photographic
@@ -1756,36 +1760,36 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Changed those private ActiveRecord methods to take optional third argument :auto instead of nil for performance optimizations. (closes #4456) [Stefan]
-* Private ActiveRecord methods add_limit!, add_joins!, and add_conditions! take an OPTIONAL third argument 'scope' (closes #4456) [Rick]
+* Private ActiveRecord methods add_limit!, add_joins!, and add_conditions! take an OPTIONAL third argument 'scope' (closes #4456) [Rick Olson]
-* DEPRECATED: Using additional attributes on has_and_belongs_to_many associations. Instead upgrade your association to be a real join model [DHH]
+* DEPRECATED: Using additional attributes on has_and_belongs_to_many associations. Instead upgrade your association to be a real join model [David Heinemeier Hansson]
-* Fixed that records returned from has_and_belongs_to_many associations with additional attributes should be marked as read only (fixes #4512) [DHH]
+* Fixed that records returned from has_and_belongs_to_many associations with additional attributes should be marked as read only (fixes #4512) [David Heinemeier Hansson]
* Do not implicitly mark recordss of has_many :through as readonly but do mark habtm records as readonly (eventually only on join tables without rich attributes). [Marcel Mollina Jr.]
-* Fixed broken OCIAdapter #4457 [schoenm@earthlink.net]
+* Fixed broken OCIAdapter #4457 [Michael Schoen]
*1.14.0* (March 27th, 2006)
* Replace 'rescue Object' with a finer grained rescue. Closes #4431. [Nicholas Seckar]
-* Fixed eager loading so that an aliased table cannot clash with a has_and_belongs_to_many join table [Rick]
+* Fixed eager loading so that an aliased table cannot clash with a has_and_belongs_to_many join table [Rick Olson]
* Add support for :include to with_scope [andrew@redlinesoftware.com]
-* Support the use of public synonyms with the Oracle adapter; required ruby-oci8 v0.1.14 #4390 [schoenm@earthlink.net]
+* Support the use of public synonyms with the Oracle adapter; required ruby-oci8 v0.1.14 #4390 [Michael Schoen]
* Change periods (.) in table aliases to _'s. Closes #4251 [jeff@ministrycentered.com]
-* Changed has_and_belongs_to_many join to INNER JOIN for Mysql 3.23.x. Closes #4348 [Rick]
+* Changed has_and_belongs_to_many join to INNER JOIN for Mysql 3.23.x. Closes #4348 [Rick Olson]
-* Fixed issue that kept :select options from being scoped [Rick]
+* Fixed issue that kept :select options from being scoped [Rick Olson]
-* Fixed db_schema_import when binary types are present #3101 [DHH]
+* Fixed db_schema_import when binary types are present #3101 [David Heinemeier Hansson]
-* Fixed that MySQL enums should always be returned as strings #3501 [DHH]
+* Fixed that MySQL enums should always be returned as strings #3501 [David Heinemeier Hansson]
* Change has_many :through to use the :source option to specify the source association. :class_name is now ignored. [Rick Olson]
@@ -1816,13 +1820,13 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
end
end
-* Fixed that schema changes while the database was open would break any connections to a SQLite database (now we reconnect if that error is throw) [DHH]
+* Fixed that schema changes while the database was open would break any connections to a SQLite database (now we reconnect if that error is throw) [David Heinemeier Hansson]
-* Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) [jonathan@bluewire.net.nz]
+* Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) [Jonathan Viney]
* Quit ignoring default :include options in has_many :through calls [Mark James]
-* Allow has_many :through associations to find the source association by setting a custom class (closes #4307) [jonathan@bluewire.net.nz]
+* Allow has_many :through associations to find the source association by setting a custom class (closes #4307) [Jonathan Viney]
* Eager Loading support added for has_many :through => :has_many associations (see below). [Rick Olson]
@@ -1842,13 +1846,13 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
belongs_to :client
end
-* Raise error when trying to select many polymorphic objects with has_many :through or :include (closes #4226) [josh@hasmanythrough.com]
+* Raise error when trying to select many polymorphic objects with has_many :through or :include (closes #4226) [Josh Susser]
-* Fixed has_many :through to include :conditions set on the :through association. closes #4020 [jonathan@bluewire.net.nz]
+* Fixed has_many :through to include :conditions set on the :through association. closes #4020 [Jonathan Viney]
-* Fix that has_many :through honors the foreign key set by the belongs_to association in the join model (closes #4259) [andylien@gmail.com / Rick]
+* Fix that has_many :through honors the foreign key set by the belongs_to association in the join model (closes #4259) [andylien@gmail.com / Rick Olson]
-* SQL Server adapter gets some love #4298 [rtomayko@gmail.com]
+* SQL Server adapter gets some love #4298 [Ryan Tomayko]
* Added OpenBase database adapter that builds on top of the http://www.spice-of-life.net/ruby-openbase/ driver. All functionality except LIMIT/OFFSET is supported #3528 [derrickspell@cdmplus.com]
@@ -1856,27 +1860,27 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
Author.find(:all, :include => { :posts => :special_comments }, :order => 'special_comments.body')
-* Add AbstractAdapter#table_alias_for to create table aliases according to the rules of the current adapter. [Rick]
+* Add AbstractAdapter#table_alias_for to create table aliases according to the rules of the current adapter. [Rick Olson]
-* Provide access to the underlying database connection through Adapter#raw_connection. Enables the use of db-specific methods without complicating the adapters. #2090 [Koz]
+* Provide access to the underlying database connection through Adapter#raw_connection. Enables the use of db-specific methods without complicating the adapters. #2090 [Michael Koziarski]
-* Remove broken attempts at handling columns with a default of 'now()' in the postgresql adapter. #2257 [Koz]
+* Remove broken attempts at handling columns with a default of 'now()' in the postgresql adapter. #2257 [Michael Koziarski]
-* Added connection#current_database that'll return of the current database (only works in MySQL, SQL Server, and Oracle so far -- please help implement for the rest of the adapters) #3663 [Tom ward]
+* Added connection#current_database that'll return of the current database (only works in MySQL, SQL Server, and Oracle so far -- please help implement for the rest of the adapters) #3663 [Tom Ward]
* Fixed that Migration#execute would have the table name prefix appended to its query #4110 [mark.imbriaco@pobox.com]
* Make all tinyint(1) variants act like boolean in mysql (tinyint(1) unsigned, etc.) [Jamis Buck]
-* Use association's :conditions when eager loading. [jeremyevans0@gmail.com] #4144
+* Use association's :conditions when eager loading. [Jeremy Evans] #4144
-* Alias the has_and_belongs_to_many join table on eager includes. #4106 [jeremyevans0@gmail.com]
+* Alias the has_and_belongs_to_many join table on eager includes. #4106 [Jeremy Evans]
This statement would normally error because the projects_developers table is joined twice, and therefore joined_on would be ambiguous.
Developer.find(:all, :include => {:projects => :developers}, :conditions => 'join_project_developers.joined_on IS NOT NULL')
-* Oracle adapter gets some love #4230 [schoenm@earthlink.net]
+* Oracle adapter gets some love #4230 [Michael Schoen]
* Changes :text to CLOB rather than BLOB [Moses Hohman]
* Fixes an issue with nil numeric length/scales (several)
@@ -1911,7 +1915,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
has_many :attachments, :as => :attachable, :dependent => :delete_all
end
-* Nicer error message on has_many :through when :through reflection can not be found. #4042 [court3nay@gmail.com]
+* Nicer error message on has_many :through when :through reflection can not be found. #4042 [court3nay]
* Upgrade to Transaction::Simple 1.3 [Jamis Buck]
@@ -1923,7 +1927,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Dynamically set allow_concurrency. #4044 [Stefan Kaes]
-* Added Base#to_xml that'll turn the current record into a XML representation [DHH]. Example:
+* Added Base#to_xml that'll turn the current record into a XML representation [David Heinemeier Hansson]. Example:
topic.to_xml
@@ -2058,9 +2062,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* CHANGED DEFAULT: set ActiveRecord::Base.allow_concurrency to false. Most AR usage is in single-threaded applications. [Jeremy Kemper]
-* Renamed the "oci" adapter to "oracle", but kept the old name as an alias #4017 [schoenm@earthlink.net]
+* Renamed the "oci" adapter to "oracle", but kept the old name as an alias #4017 [Michael Schoen]
-* Fixed that Base.save should always return false if the save didn't succeed, including if it has halted by before_save's #1861, #2477 [DHH]
+* Fixed that Base.save should always return false if the save didn't succeed, including if it has halted by before_save's #1861, #2477 [David Heinemeier Hansson]
* Speed up class -> connection caching and stale connection verification. #3979 [Stefan Kaes]
@@ -2068,7 +2072,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Added that fixtures to placed in subdirectories of the main fixture files are also loaded #3937 [dblack@wobblini.net]
-* Define attribute query methods to avoid method_missing calls. #3677 [jonathan@bluewire.net.nz]
+* Define attribute query methods to avoid method_missing calls. #3677 [Jonathan Viney]
* ActiveRecord::Base.remove_connection explicitly closes database connections and doesn't corrupt the connection cache. Introducing the disconnect! instance method for the PostgreSQL, MySQL, and SQL Server adapters; implementations for the others are welcome. #3591 [Simon Stapleton, Tom Ward]
@@ -2092,7 +2096,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Fixed validates_length_of to work on UTF-8 strings by using characters instead of bytes #3699 [Masao Mutoh]
-* Fixed that reflections would bleed across class boundaries in single-table inheritance setups #3796 [lars@pind.com]
+* Fixed that reflections would bleed across class boundaries in single-table inheritance setups #3796 [Lars Pind]
* Added calculations: Base.count, Base.average, Base.sum, Base.minimum, Base.maxmium, and the generic Base.calculate. All can be used with :group and :having. Calculations and statitics need no longer require custom SQL. #3958 [Rick Olson]. Examples:
@@ -2101,7 +2105,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
Person.maximum :age
Person.sum :salary, :group => :last_name
-* Renamed Errors#count to Errors#size but kept an alias for the old name (and included an alias for length too) #3920 [contact@lukeredpath.co.uk]
+* Renamed Errors#count to Errors#size but kept an alias for the old name (and included an alias for length too) #3920 [Luke Redpath]
* Reflections don't attempt to resolve module nesting of association classes. Simplify type computation. [Jeremy Kemper]
@@ -2109,7 +2113,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Fixed that the schema_info table used by ActiveRecord::Schema.define should respect table pre- and suffixes #3834 [rubyonrails@atyp.de]
-* Added :select option to Base.count that'll allow you to select something else than * to be counted on. Especially important for count queries using DISTINCT #3839 [skaes]
+* Added :select option to Base.count that'll allow you to select something else than * to be counted on. Especially important for count queries using DISTINCT #3839 [Stefan Kaes]
* Correct syntax error in mysql DDL, and make AAACreateTablesTest run first [Bob Silva]
@@ -2125,19 +2129,19 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Fix problems with count when used with :include [Jeremy Hopple and Kevin Clark]
-* ActiveRecord::RecordInvalid now states which validations failed in its default error message [Tobias Luetke]
+* ActiveRecord::RecordInvalid now states which validations failed in its default error message [Tobias Lütke]
-* Using AssociationCollection#build with arrays of hashes should call build, not create [DHH]
+* Using AssociationCollection#build with arrays of hashes should call build, not create [David Heinemeier Hansson]
* Remove definition of reloadable? from ActiveRecord::Base to make way for new Reloadable code. [Nicholas Seckar]
* Fixed schema handling for DB2 adapter that didn't work: an initial schema could be set, but it wasn't used when getting tables and indexes #3678 [Maik Schmidt]
-* Support the :column option for remove_index with the PostgreSQL adapter. #3661 [shugo@ruby-lang.org]
+* Support the :column option for remove_index with the PostgreSQL adapter. #3661 [Shugo Maeda]
* Add documentation for add_index and remove_index. #3600 [Manfred Stienstra <m.stienstra@fngtps.com>]
-* If the OCI library is not available, raise an exception indicating as much. #3593 [schoenm@earthlink.net]
+* If the OCI library is not available, raise an exception indicating as much. #3593 [Michael Schoen]
* Add explicit :order in finder tests as postgresql orders results differently by default. #3577. [Rick Olson]
@@ -2145,7 +2149,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Show a meaningful error when the DB2 adapter cannot be loaded due to missing dependencies. [Nicholas Seckar]
-* Make .count work for has_many associations with multi line finder sql [schoenm@earthlink.net]
+* Make .count work for has_many associations with multi line finder sql [Michael Schoen]
* Add AR::Base.base_class for querying the ancestor AR::Base subclass [Jamis Buck]
@@ -2153,13 +2157,13 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Don't hardcode 'id' in acts as list. [ror@philippeapril.com]
-* Fix date errors for SQLServer in association tests. #3406 [kevin.clark@gmal.com]
+* Fix date errors for SQLServer in association tests. #3406 [Kevin Clark]
* Escape database name in MySQL adapter when creating and dropping databases. #3409 [anna@wota.jp]
* Disambiguate table names for columns in validates_uniquness_of's WHERE clause. #3423 [alex.borovsky@gmail.com]
-* .with_scope imposed create parameters now bypass attr_protected [Tobias Luetke]
+* .with_scope imposed create parameters now bypass attr_protected [Tobias Lütke]
* Don't raise an exception when there are more keys than there are named bind variables when sanitizing conditions. [Marcel Molina Jr.]
@@ -2167,25 +2171,25 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Sanitize scoped conditions. [Marcel Molina Jr.]
-* Added option to Base.reflection_of_all_associations to specify a specific association to scope the call. For example Base.reflection_of_all_associations(:has_many) [DHH]
+* Added option to Base.reflection_of_all_associations to specify a specific association to scope the call. For example Base.reflection_of_all_associations(:has_many) [David Heinemeier Hansson]
-* Added ActiveRecord::SchemaDumper.ignore_tables which tells SchemaDumper which tables to ignore. Useful for tables with funky column like the ones required for tsearch2. [TobiasLuetke]
+* Added ActiveRecord::SchemaDumper.ignore_tables which tells SchemaDumper which tables to ignore. Useful for tables with funky column like the ones required for tsearch2. [Tobias Lütke]
-* SchemaDumper now doesn't fail anymore when there are unknown column types in the schema. Instead the table is ignored and a Comment is left in the schema.rb. [TobiasLuetke]
+* SchemaDumper now doesn't fail anymore when there are unknown column types in the schema. Instead the table is ignored and a Comment is left in the schema.rb. [Tobias Lütke]
* Fixed that saving a model with multiple habtm associations would only save the first one. #3244 [yanowitz-rubyonrails@quantumfoam.org, Florian Weber]
* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron]
-* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Luetke]
+* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Lütke]
-* made method missing delegation to class methods on relation target work on :through associations. [Tobias Luetke]
+* made method missing delegation to class methods on relation target work on :through associations. [Tobias Lütke]
-* made .find() work on :through relations. [Tobias Luetke]
+* made .find() work on :through relations. [Tobias Lütke]
* Fix typo in association docs. #3296. [Blair Zajac]
-* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Luetke]
+* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Lütke]
*1.13.2* (December 13th, 2005)
@@ -2193,7 +2197,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* MySQL: allow encoding option for mysql.rb driver. [Jeremy Kemper]
-* Added option inheritance for find calls on has_and_belongs_to_many and has_many assosociations [DHH]. Example:
+* Added option inheritance for find calls on has_and_belongs_to_many and has_many assosociations [David Heinemeier Hansson]. Example:
class Post
has_many :recent_comments, :class_name => "Comment", :limit => 10, :include => :author
@@ -2203,7 +2207,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
post.recent_comments.find(:all, :limit => nil) # Uses no limit but include authors
post.recent_comments.find(:all, :limit => nil, :include => nil) # Uses no limit and doesn't include authors
-* Added option to specify :group, :limit, :offset, and :select options from find on has_and_belongs_to_many and has_many assosociations [DHH]
+* Added option to specify :group, :limit, :offset, and :select options from find on has_and_belongs_to_many and has_many assosociations [David Heinemeier Hansson]
* MySQL: fixes for the bundled mysql.rb driver. #3160 [Justin Forder]
@@ -2231,9 +2235,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat. Eliminate usage of mysql.ping because it doesn't guarantee reconnect. Explicitly close and reopen the connection instead. [Jeremy Kemper]
-* Added preliminary support for polymorphic associations [DHH]
+* Added preliminary support for polymorphic associations [David Heinemeier Hansson]
-* Added preliminary support for join models [DHH]
+* Added preliminary support for join models [David Heinemeier Hansson]
* Allow validate_uniqueness_of to be scoped by more than just one column. #1559. [jeremy@jthopple.com, Marcel Molina Jr.]
@@ -2259,7 +2263,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Add tasks to create, drop and rebuild the MySQL and PostgreSQL test databases. [Marcel Molina Jr.]
-* Correct boolean handling in generated reader methods. #2945 [don.park@gmail.com, Stefan Kaes]
+* Correct boolean handling in generated reader methods. #2945 [Don Park, Stefan Kaes]
* Don't generate read methods for columns whose names are not valid ruby method names. #2946 [Stefan Kaes]
@@ -2307,7 +2311,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Fix sqlite adaptor's detection of missing dbfile or database declaration. [Nicholas Seckar]
-* Fixed acts_as_list for definitions without an explicit :order #2803 [jonathan@bluewire.net.nz]
+* Fixed acts_as_list for definitions without an explicit :order #2803 [Jonathan Viney]
* Upgrade bundled ruby-mysql 0.2.4 with mysql411 shim (see #440) to ruby-mysql 0.2.6 with a patchset for 4.1 protocol support. Local change [301] is now a part of the main driver; reapplied local change [2182]. Removed GC.start from Result.free. [tommy@tmtm.org, akuroda@gmail.com, Doug Fales <doug.fales@gmail.com>, Jeremy Kemper]
@@ -2326,7 +2330,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Fixed faulty regex in get_table_name method (SQLServerAdapter) #2639 [Ryan Tomayko]
-* Added :include as an option for association declarations [DHH]. Example:
+* Added :include as an option for association declarations [David Heinemeier Hansson]. Example:
has_many :posts, :include => [ :author, :comments ]
@@ -2341,7 +2345,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Fixed that SQL Server should ignore :size declarations on anything but integer and string in the agnostic schema representation #2756 [Ryan Tomayko]
-* Added constrain scoping for creates using a hash of attributes bound to the :creation key [DHH]. Example:
+* Added constrain scoping for creates using a hash of attributes bound to the :creation key [David Heinemeier Hansson]. Example:
Comment.constrain(:creation => { :post_id => 5 }) do
# Associated with :post_id
@@ -2353,7 +2357,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
# If the tag doesn't exist, a new one is created that's associated with the person
person.tags.find_or_create_by_name("Summer")
-* Added find_or_create_by_X as a second type of dynamic finder that'll create the record if it doesn't already exist [DHH]. Example:
+* Added find_or_create_by_X as a second type of dynamic finder that'll create the record if it doesn't already exist [David Heinemeier Hansson]. Example:
# No 'Summer' tag exists
Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
@@ -2361,7 +2365,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
# Now the 'Summer' tag does exist
Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
-* Added extension capabilities to has_many and has_and_belongs_to_many proxies [DHH]. Example:
+* Added extension capabilities to has_many and has_and_belongs_to_many proxies [David Heinemeier Hansson]. Example:
class Account < ActiveRecord::Base
has_many :people do
@@ -2380,9 +2384,9 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
Note that the anoymous module must be declared using brackets, not do/end (due to order of evaluation).
-* Omit internal dtproperties table from SQLServer table list. #2729 [rtomayko@gmail.com]
+* Omit internal dtproperties table from SQLServer table list. #2729 [Ryan Tomayko]
-* Quote column names in generated SQL. #2728 [rtomayko@gmail.com]
+* Quote column names in generated SQL. #2728 [Ryan Tomayko]
* Correct the pure-Ruby MySQL 4.1.1 shim's version test. #2718 [Jeremy Kemper]
@@ -2396,7 +2400,7 @@ during calendar reform. #7649, #7724 [fedot, Geoff Buesing]
* Worked around that connection can't be reset if allow_concurrency is off. #2648 [Michael Schoen <schoenm@earthlink.net>]
-* Fixed SQL Server adapter to pass even more tests and do even better #2634 [rtomayko@gmail.com]
+* Fixed SQL Server adapter to pass even more tests and do even better #2634 [Ryan Tomayko]
* Fixed SQL Server adapter so it honors options[:conditions] when applying :limits #1978 [Tom Ward]
@@ -2409,18 +2413,18 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added :offset and :limit to the kinds of options that Base.constrain can use #2466 [duane.johnson@gmail.com]
-* Fixed handling of nil number columns on Oracle and cleaned up tests for Oracle in general #2555 [schoenm@earthlink.net]
+* Fixed handling of nil number columns on Oracle and cleaned up tests for Oracle in general #2555 [Michael Schoen]
* Added quoted_true and quoted_false methods and tables to db2_adapter and cleaned up tests for DB2 #2493, #2624 [maik schmidt]
*1.12.2* (October 26th, 2005)
-* Allow symbols to rename columns when using SQLite adapter. #2531 [kevin.clark@gmail.com]
+* Allow symbols to rename columns when using SQLite adapter. #2531 [Kevin Clark]
* Map Active Record time to SQL TIME. #2575, #2576 [Robby Russell <robby@planetargon.com>]
-* Clarify semantics of ActiveRecord::Base#respond_to? #2560 [skaes@web.de]
+* Clarify semantics of ActiveRecord::Base#respond_to? #2560 [Stefan Kaes]
* Fixed Association#clear for associations which have not yet been accessed. #2524 [Patrick Lenz <patrick@lenz.sh>]
@@ -2474,25 +2478,25 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Add test coverage for content_columns. #2432. [coffee2code]
-* Speed up for unthreaded environments. #2431. [skaes@web.de]
+* Speed up for unthreaded environments. #2431. [Stefan Kaes]
-* Optimization for Mysql selects using mysql-ruby extension greater than 2.6.3. #2426. [skaes@web.de]
+* Optimization for Mysql selects using mysql-ruby extension greater than 2.6.3. #2426. [Stefan Kaes]
-* Speed up the setting of table_name. #2428. [skaes@web.de]
+* Speed up the setting of table_name. #2428. [Stefan Kaes]
-* Optimize instantiation of STI subclass records. In partial fullfilment of #1236. [skaes@web.de]
+* Optimize instantiation of STI subclass records. In partial fullfilment of #1236. [Stefan Kaes]
* Fix typo of 'constrains' to 'contraints'. #2069. [Michael Schuerig <michael@schuerig.de>]
-* Optimization refactoring for add_limit_offset!. In partial fullfilment of #1236. [skaes@web.de]
+* Optimization refactoring for add_limit_offset!. In partial fullfilment of #1236. [Stefan Kaes]
* Add ability to get all siblings, including the current child, with acts_as_tree. Recloses #2140. [Michael Schuerig <michael@schuerig.de>]
-* Add geometric type for postgresql adapter. #2233 [akaspick@gmail.com]
+* Add geometric type for postgresql adapter. #2233 [Andrew Kaspick]
-* Add option (true by default) to generate reader methods for each attribute of a record to avoid the overhead of calling method missing. In partial fullfilment of #1236. [skaes@web.de]
+* Add option (true by default) to generate reader methods for each attribute of a record to avoid the overhead of calling method missing. In partial fullfilment of #1236. [Stefan Kaes]
-* Add convenience predicate methods on Column class. In partial fullfilment of #1236. [skaes@web.de]
+* Add convenience predicate methods on Column class. In partial fullfilment of #1236. [Stefan Kaes]
* Raise errors when invalid hash keys are passed to ActiveRecord::Base.find. #2363 [Chad Fowler <chad@chadfowler.com>, Nicholas Seckar]
@@ -2517,7 +2521,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Make update_attribute use the same writer method that update_attributes uses.
#2237 [trevor@protocool.com]
-* Make migrations honor table name prefixes and suffixes. #2298 [Jakob S, Marcel Molina]
+* Make migrations honor table name prefixes and suffixes. #2298 [Jakob Skjerning, Marcel Molina Jr.]
* Correct and optimize PostgreSQL bytea escaping. #1745, #1837 [dave@cherryville.org, ken@miriamtech.com, bellis@deepthought.org]
@@ -2525,7 +2529,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Standardize the interpretation of boolean columns in the Mysql and Sqlite adapters. (Use MysqlAdapter.emulate_booleans = false to disable this behavior)
-* Added new symbol-driven approach to activating observers with Base#observers= [DHH]. Example:
+* Added new symbol-driven approach to activating observers with Base#observers= [David Heinemeier Hansson]. Example:
ActiveRecord::Base.observers = :cacher, :garbage_collector
@@ -2541,19 +2545,19 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed that the create_x method from belongs_to wouldn't save the association properly #2042 [Florian Weber]
-* Fixed saving a record with two unsaved belongs_to associations pointing to the same object #2023 [Tobias Luetke]
+* Fixed saving a record with two unsaved belongs_to associations pointing to the same object #2023 [Tobias Lütke]
* Improved migrations' behavior when the schema_info table is empty. [Nicholas Seckar]
* Fixed that Observers didn't observe sub-classes #627 [Florian Weber]
-* Fix eager loading error messages, allow :include to specify tables using strings or symbols. Closes #2222 [Marcel Molina]
+* Fix eager loading error messages, allow :include to specify tables using strings or symbols. Closes #2222 [Marcel Molina Jr.]
* Added check for RAILS_CONNECTION_ADAPTERS on startup and only load the connection adapters specified within if its present (available in Rails through config.connection_adapters using the new config) #1958 [skae]
* Fixed various problems with has_and_belongs_to_many when using customer finder_sql #2094 [Florian Weber]
-* Added better exception error when unknown column types are used with migrations #1814 [fbeausoleil@ftml.net]
+* Added better exception error when unknown column types are used with migrations #1814 [François Beausoleil]
* Fixed "connection lost" issue with the bundled Ruby/MySQL driver (would kill the app after 8 hours of inactivity) #2163, #428 [kajism@yahoo.com]
@@ -2569,13 +2573,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Make sure the schema_info table is created before querying the current version #1903
-* Fixtures ignore table name prefix and suffix #1987 [Jakob S]
+* Fixtures ignore table name prefix and suffix #1987 [Jakob Skjerning]
-* Add documentation for index_type argument to add_index method for migrations #2005 [blaine@odeo.com]
+* Add documentation for index_type argument to add_index method for migrations #2005 [Blaine]
* Modify read_attribute to allow a symbol argument #2024 [Ken Kunz]
-* Make destroy return self #1913 [sebastian.kanthak@muehlheim.de]
+* Make destroy return self #1913 [Sebastian Kanthak]
* Fix typo in validations documentation #1938 [court3nay]
@@ -2597,7 +2601,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed the handling of multiple blob columns in Oracle if one or more of them are null #1798
-* Added support for calling constrained class methods on has_many and has_and_belongs_to_many collections #1764 [Tobias Luetke]
+* Added support for calling constrained class methods on has_many and has_and_belongs_to_many collections #1764 [Tobias Lütke]
class Comment < AR:B
def self.search(q)
@@ -2629,19 +2633,19 @@ in effect. Added :readonly finder constraint. Calling an association collectio
*1.11.1* (11 July, 2005)
-* Added support for limit and offset with eager loading of has_one and belongs_to associations. Using the options with has_many and has_and_belongs_to_many associations will now raise an ActiveRecord::ConfigurationError #1692 [Rick Olsen]
+* Added support for limit and offset with eager loading of has_one and belongs_to associations. Using the options with has_many and has_and_belongs_to_many associations will now raise an ActiveRecord::ConfigurationError #1692 [Rick Olson]
* Fixed that assume_bottom_position (in acts_as_list) could be called on items already last in the list and they would move one position away from the list #1648 [tyler@kianta.com]
* Added ActiveRecord::Base.threaded_connections flag to turn off 1-connection per thread (required for thread safety). By default it's on, but WEBrick in Rails need it off #1685 [Sam Stephenson]
-* Correct reflected table name for singular associations. #1688 [court3nay@gmail.com]
+* Correct reflected table name for singular associations. #1688 [court3nay]
* Fixed optimistic locking with SQL Server #1660 [tom@popdog.net]
* Added ActiveRecord::Migrator.migrate that can figure out whether to go up or down based on the target version and the current
-* Added better error message for "packets out of order" #1630 [courtenay]
+* Added better error message for "packets out of order" #1630 [court3nay]
* Fixed first run of "rake migrate" on PostgreSQL by not expecting a return value on the id #1640
@@ -2652,7 +2656,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Changed logging of SQL statements to use the DEBUG level instead of INFO
-* Added new Migrations framework for describing schema transformations in a way that can be easily applied across multiple databases #1604 [Tobias Luetke] See documentation under ActiveRecord::Migration and the additional support in the Rails rakefile/generator.
+* Added new Migrations framework for describing schema transformations in a way that can be easily applied across multiple databases #1604 [Tobias Lütke] See documentation under ActiveRecord::Migration and the additional support in the Rails rakefile/generator.
* Added callback hooks to association collections #1549 [Florian Weber]. Example:
@@ -2675,13 +2679,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Speed up ActiveRecord#method_missing for the common case (read_attribute).
-* Only notify observers on after_find and after_initialize if these methods are defined on the model. #1235 [skaes@web.de]
+* Only notify observers on after_find and after_initialize if these methods are defined on the model. #1235 [Stefan Kaes]
* Fixed that single-table inheritance sub-classes couldn't be used to limit the result set with eager loading #1215 [Chris McGrath]
* Fixed validates_numericality_of to work with overrided getter-method when :allow_nil is on #1316 [raidel@onemail.at]
-* Added roots, root, and siblings to the batch of methods added by acts_as_tree #1541 [michael@schuerig.de]
+* Added roots, root, and siblings to the batch of methods added by acts_as_tree #1541 [Michael Schuerig]
* Added support for limit/offset with the MS SQL Server driver so that pagination will now work #1569 [DeLynn Berry]
@@ -2703,7 +2707,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
:conditions => 'project_id=1'
)
-* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina]
+* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina Jr.]
* Fixed that clear_association_cache doesn't delete new associations on new records (so you can safely place new records in the session with Action Pack without having new associations wiped) #1494 [cluon]
@@ -2730,7 +2734,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Corrected typo in find SQL for has_and_belongs_to_many. #1312 [ben@bensinclair.com]
-* Fixed sanitized conditions for has_many finder method. #1281 [jackc@hylesanderson.com, pragdave, Tobias Luetke]
+* Fixed sanitized conditions for has_many finder method. #1281 [jackc@hylesanderson.com, pragdave, Tobias Lütke]
* Comprehensive PostgreSQL schema support. Use the optional schema_search_path directive in database.yml to give a comma-separated list of schemas to search for your tables. This allows you, for example, to have tables in a shared schema without having to use a custom table name. See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html to learn more. #827 [dave@cherryville.org]
@@ -2788,15 +2792,15 @@ in effect. Added :readonly finder constraint. Calling an association collectio
# SELECT * FROM topics WHERE title IN ('First', 'Second')
Topic.find_all_by_title(["First", "Second"])
-* Added compatibility with camelCase column names for dynamic finders #533 [Dee.Zsombor]
+* Added compatibility with camelCase column names for dynamic finders #533 [Dee Zsombor]
-* Fixed extraneous comma in count() function that made it not work with joins #1156 [jarkko/Dee.Zsombor]
+* Fixed extraneous comma in count() function that made it not work with joins #1156 [Jarkko Laine/Dee Zsombor]
* Fixed incompatibility with Base#find with an array of ids that would fail when using eager loading #1186 [Alisdair McDiarmid]
* Fixed that validate_length_of lost :on option when :within was specified #1195 [jhosteny@mac.com]
-* Added encoding and min_messages options for PostgreSQL #1205 [shugo]. Configuration example:
+* Added encoding and min_messages options for PostgreSQL #1205 [Shugo Maeda]. Configuration example:
development:
adapter: postgresql
@@ -2854,7 +2858,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
a single query. A good use case for this is a threaded post system, where you want
to display every reply to a comment without multiple selects.
-* Added Base.save! that attempts to save the record just like Base.save but will raise a RecordInvalid exception instead of returning false if the record is not valid [After much pestering from Dave Thomas]
+* Added Base.save! that attempts to save the record just like Base.save but will raise a RecordInvalid exception instead of returning false if the record is not valid [Dave Thomas]
* Fixed PostgreSQL usage of fixtures with regards to public schemas and table names with dots #962 [gnuman1@gmail.com]
@@ -2884,7 +2888,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed counter_sql when no records exist in database for PostgreSQL (would give error, not 0) #1039 [Caleb Tennis]
-* Fixed that benchmarking times for rendering included db runtimes #987 [skaes@web.de]
+* Fixed that benchmarking times for rendering included db runtimes #987 [Stefan Kaes]
* Fixed boolean queries for t/f fields in PostgreSQL #995 [dave@cherryville.org]
@@ -2892,9 +2896,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed auto-stamping of dates (created_on/updated_on) for PostgreSQL #985 [dave@cherryville.org]
-* Fixed Base.silence/benchmark to only log if a logger has been configured #986 [skaes@web.de]
+* Fixed Base.silence/benchmark to only log if a logger has been configured #986 [Stefan Kaes]
-* Added a join parameter as the third argument to Base.find_first and as the second to Base.count #426, #988 [skaes@web.de]
+* Added a join parameter as the third argument to Base.find_first and as the second to Base.count #426, #988 [Stefan Kaes]
* Fixed bug in Base#hash method that would treat records with the same string-based id as different [Dave Thomas]
@@ -2980,7 +2984,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
account.save # => CC with id = 12 is destroyed
-* Added validates_numericality_of #716 [skanthak/c.r.mcgrath]. Docuemntation:
+* Added validates_numericality_of #716 [Sebastian Kanthak/Chris McGrath]. Docuemntation:
Validates whether the value of the specified attribute is numeric by trying to convert it to
a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression
@@ -3050,7 +3054,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
4. Added logic to the simplified_type method that allows the database to specify the scale of float data.
5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string.
-* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow@yahoo.com]
+* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow]
* Added validates_each that validates each specified attribute against a block #610 [Jeremy Kemper]. Example:
@@ -3233,17 +3237,17 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed that that multiparameter assignment doesn't work with aggregations (closes #4620) [Lars Pind]
-* Enable Limit/Offset in Calculations (closes #4558) [lmarlow@yahoo.com]
+* Enable Limit/Offset in Calculations (closes #4558) [lmarlow]
-* Fixed that loading including associations returns all results if Load IDs For Limited Eager Loading returns none (closes #4528) [Rick]
+* Fixed that loading including associations returns all results if Load IDs For Limited Eager Loading returns none (closes #4528) [Rick Olson]
* Fixed HasManyAssociation#find bugs when :finder_sql is set #4600 [lagroue@free.fr]
-* Allow AR::Base#respond_to? to behave when @attributes is nil [zenspider]
+* Allow AR::Base#respond_to? to behave when @attributes is nil [Ryan Davis]
-* Support eager includes when going through a polymorphic has_many association. [Rick]
+* Support eager includes when going through a polymorphic has_many association. [Rick Olson]
-* Added support for eagerly including polymorphic has_one associations. (closes #4525) [Rick]
+* Added support for eagerly including polymorphic has_one associations. (closes #4525) [Rick Olson]
class Post < ActiveRecord::Base
has_one :tagging, :as => :taggable
@@ -3251,9 +3255,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
Post.find :all, :include => :tagging
-* Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many [Rick]
+* Added descriptive error messages for invalid has_many :through associations: going through :has_one or :has_and_belongs_to_many [Rick Olson]
-* Added support for going through a polymorphic has_many association: (closes #4401) [Rick]
+* Added support for going through a polymorphic has_many association: (closes #4401) [Rick Olson]
class PhotoCollection < ActiveRecord::Base
has_many :photos, :as => :photographic
@@ -3271,36 +3275,36 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Changed those private ActiveRecord methods to take optional third argument :auto instead of nil for performance optimizations. (closes #4456) [Stefan]
-* Private ActiveRecord methods add_limit!, add_joins!, and add_conditions! take an OPTIONAL third argument 'scope' (closes #4456) [Rick]
+* Private ActiveRecord methods add_limit!, add_joins!, and add_conditions! take an OPTIONAL third argument 'scope' (closes #4456) [Rick Olson]
-* DEPRECATED: Using additional attributes on has_and_belongs_to_many associations. Instead upgrade your association to be a real join model [DHH]
+* DEPRECATED: Using additional attributes on has_and_belongs_to_many associations. Instead upgrade your association to be a real join model [David Heinemeier Hansson]
-* Fixed that records returned from has_and_belongs_to_many associations with additional attributes should be marked as read only (fixes #4512) [DHH]
+* Fixed that records returned from has_and_belongs_to_many associations with additional attributes should be marked as read only (fixes #4512) [David Heinemeier Hansson]
* Do not implicitly mark recordss of has_many :through as readonly but do mark habtm records as readonly (eventually only on join tables without rich attributes). [Marcel Mollina Jr.]
-* Fixed broken OCIAdapter #4457 [schoenm@earthlink.net]
+* Fixed broken OCIAdapter #4457 [Michael Schoen]
*1.14.0* (March 27th, 2006)
* Replace 'rescue Object' with a finer grained rescue. Closes #4431. [Nicholas Seckar]
-* Fixed eager loading so that an aliased table cannot clash with a has_and_belongs_to_many join table [Rick]
+* Fixed eager loading so that an aliased table cannot clash with a has_and_belongs_to_many join table [Rick Olson]
* Add support for :include to with_scope [andrew@redlinesoftware.com]
-* Support the use of public synonyms with the Oracle adapter; required ruby-oci8 v0.1.14 #4390 [schoenm@earthlink.net]
+* Support the use of public synonyms with the Oracle adapter; required ruby-oci8 v0.1.14 #4390 [Michael Schoen]
* Change periods (.) in table aliases to _'s. Closes #4251 [jeff@ministrycentered.com]
-* Changed has_and_belongs_to_many join to INNER JOIN for Mysql 3.23.x. Closes #4348 [Rick]
+* Changed has_and_belongs_to_many join to INNER JOIN for Mysql 3.23.x. Closes #4348 [Rick Olson]
-* Fixed issue that kept :select options from being scoped [Rick]
+* Fixed issue that kept :select options from being scoped [Rick Olson]
-* Fixed db_schema_import when binary types are present #3101 [DHH]
+* Fixed db_schema_import when binary types are present #3101 [David Heinemeier Hansson]
-* Fixed that MySQL enums should always be returned as strings #3501 [DHH]
+* Fixed that MySQL enums should always be returned as strings #3501 [David Heinemeier Hansson]
* Change has_many :through to use the :source option to specify the source association. :class_name is now ignored. [Rick Olson]
@@ -3331,13 +3335,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio
end
end
-* Fixed that schema changes while the database was open would break any connections to a SQLite database (now we reconnect if that error is throw) [DHH]
+* Fixed that schema changes while the database was open would break any connections to a SQLite database (now we reconnect if that error is throw) [David Heinemeier Hansson]
-* Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) [jonathan@bluewire.net.nz]
+* Don't classify the has_one class when eager loading, it is already singular. Add tests. (closes #4117) [Jonathan Viney]
* Quit ignoring default :include options in has_many :through calls [Mark James]
-* Allow has_many :through associations to find the source association by setting a custom class (closes #4307) [jonathan@bluewire.net.nz]
+* Allow has_many :through associations to find the source association by setting a custom class (closes #4307) [Jonathan Viney]
* Eager Loading support added for has_many :through => :has_many associations (see below). [Rick Olson]
@@ -3357,13 +3361,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio
belongs_to :client
end
-* Raise error when trying to select many polymorphic objects with has_many :through or :include (closes #4226) [josh@hasmanythrough.com]
+* Raise error when trying to select many polymorphic objects with has_many :through or :include (closes #4226) [Josh Susser]
-* Fixed has_many :through to include :conditions set on the :through association. closes #4020 [jonathan@bluewire.net.nz]
+* Fixed has_many :through to include :conditions set on the :through association. closes #4020 [Jonathan Viney]
-* Fix that has_many :through honors the foreign key set by the belongs_to association in the join model (closes #4259) [andylien@gmail.com / Rick]
+* Fix that has_many :through honors the foreign key set by the belongs_to association in the join model (closes #4259) [andylien@gmail.com / Rick Olson]
-* SQL Server adapter gets some love #4298 [rtomayko@gmail.com]
+* SQL Server adapter gets some love #4298 [Ryan Tomayko]
* Added OpenBase database adapter that builds on top of the http://www.spice-of-life.net/ruby-openbase/ driver. All functionality except LIMIT/OFFSET is supported #3528 [derrickspell@cdmplus.com]
@@ -3371,27 +3375,27 @@ in effect. Added :readonly finder constraint. Calling an association collectio
Author.find(:all, :include => { :posts => :special_comments }, :order => 'special_comments.body')
-* Add AbstractAdapter#table_alias_for to create table aliases according to the rules of the current adapter. [Rick]
+* Add AbstractAdapter#table_alias_for to create table aliases according to the rules of the current adapter. [Rick Olson]
-* Provide access to the underlying database connection through Adapter#raw_connection. Enables the use of db-specific methods without complicating the adapters. #2090 [Koz]
+* Provide access to the underlying database connection through Adapter#raw_connection. Enables the use of db-specific methods without complicating the adapters. #2090 [Michael Koziarski]
-* Remove broken attempts at handling columns with a default of 'now()' in the postgresql adapter. #2257 [Koz]
+* Remove broken attempts at handling columns with a default of 'now()' in the postgresql adapter. #2257 [Michael Koziarski]
-* Added connection#current_database that'll return of the current database (only works in MySQL, SQL Server, and Oracle so far -- please help implement for the rest of the adapters) #3663 [Tom ward]
+* Added connection#current_database that'll return of the current database (only works in MySQL, SQL Server, and Oracle so far -- please help implement for the rest of the adapters) #3663 [Tom Ward]
* Fixed that Migration#execute would have the table name prefix appended to its query #4110 [mark.imbriaco@pobox.com]
* Make all tinyint(1) variants act like boolean in mysql (tinyint(1) unsigned, etc.) [Jamis Buck]
-* Use association's :conditions when eager loading. [jeremyevans0@gmail.com] #4144
+* Use association's :conditions when eager loading. [Jeremy Evans] #4144
-* Alias the has_and_belongs_to_many join table on eager includes. #4106 [jeremyevans0@gmail.com]
+* Alias the has_and_belongs_to_many join table on eager includes. #4106 [Jeremy Evans]
This statement would normally error because the projects_developers table is joined twice, and therefore joined_on would be ambiguous.
Developer.find(:all, :include => {:projects => :developers}, :conditions => 'join_project_developers.joined_on IS NOT NULL')
-* Oracle adapter gets some love #4230 [schoenm@earthlink.net]
+* Oracle adapter gets some love #4230 [Michael Schoen]
* Changes :text to CLOB rather than BLOB [Moses Hohman]
* Fixes an issue with nil numeric length/scales (several)
@@ -3426,7 +3430,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
has_many :attachments, :as => :attachable, :dependent => :delete_all
end
-* Nicer error message on has_many :through when :through reflection can not be found. #4042 [court3nay@gmail.com]
+* Nicer error message on has_many :through when :through reflection can not be found. #4042 [court3nay]
* Upgrade to Transaction::Simple 1.3 [Jamis Buck]
@@ -3438,7 +3442,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Dynamically set allow_concurrency. #4044 [Stefan Kaes]
-* Added Base#to_xml that'll turn the current record into a XML representation [DHH]. Example:
+* Added Base#to_xml that'll turn the current record into a XML representation [David Heinemeier Hansson]. Example:
topic.to_xml
@@ -3573,9 +3577,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* CHANGED DEFAULT: set ActiveRecord::Base.allow_concurrency to false. Most AR usage is in single-threaded applications. [Jeremy Kemper]
-* Renamed the "oci" adapter to "oracle", but kept the old name as an alias #4017 [schoenm@earthlink.net]
+* Renamed the "oci" adapter to "oracle", but kept the old name as an alias #4017 [Michael Schoen]
-* Fixed that Base.save should always return false if the save didn't succeed, including if it has halted by before_save's #1861, #2477 [DHH]
+* Fixed that Base.save should always return false if the save didn't succeed, including if it has halted by before_save's #1861, #2477 [David Heinemeier Hansson]
* Speed up class -> connection caching and stale connection verification. #3979 [Stefan Kaes]
@@ -3583,7 +3587,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added that fixtures to placed in subdirectories of the main fixture files are also loaded #3937 [dblack@wobblini.net]
-* Define attribute query methods to avoid method_missing calls. #3677 [jonathan@bluewire.net.nz]
+* Define attribute query methods to avoid method_missing calls. #3677 [Jonathan Viney]
* ActiveRecord::Base.remove_connection explicitly closes database connections and doesn't corrupt the connection cache. Introducing the disconnect! instance method for the PostgreSQL, MySQL, and SQL Server adapters; implementations for the others are welcome. #3591 [Simon Stapleton, Tom Ward]
@@ -3607,7 +3611,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed validates_length_of to work on UTF-8 strings by using characters instead of bytes #3699 [Masao Mutoh]
-* Fixed that reflections would bleed across class boundaries in single-table inheritance setups #3796 [lars@pind.com]
+* Fixed that reflections would bleed across class boundaries in single-table inheritance setups #3796 [Lars Pind]
* Added calculations: Base.count, Base.average, Base.sum, Base.minimum, Base.maxmium, and the generic Base.calculate. All can be used with :group and :having. Calculations and statitics need no longer require custom SQL. #3958 [Rick Olson]. Examples:
@@ -3616,7 +3620,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
Person.maximum :age
Person.sum :salary, :group => :last_name
-* Renamed Errors#count to Errors#size but kept an alias for the old name (and included an alias for length too) #3920 [contact@lukeredpath.co.uk]
+* Renamed Errors#count to Errors#size but kept an alias for the old name (and included an alias for length too) #3920 [Luke Redpath]
* Reflections don't attempt to resolve module nesting of association classes. Simplify type computation. [Jeremy Kemper]
@@ -3624,7 +3628,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed that the schema_info table used by ActiveRecord::Schema.define should respect table pre- and suffixes #3834 [rubyonrails@atyp.de]
-* Added :select option to Base.count that'll allow you to select something else than * to be counted on. Especially important for count queries using DISTINCT #3839 [skaes]
+* Added :select option to Base.count that'll allow you to select something else than * to be counted on. Especially important for count queries using DISTINCT #3839 [Stefan Kaes]
* Correct syntax error in mysql DDL, and make AAACreateTablesTest run first [Bob Silva]
@@ -3640,19 +3644,19 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fix problems with count when used with :include [Jeremy Hopple and Kevin Clark]
-* ActiveRecord::RecordInvalid now states which validations failed in its default error message [Tobias Luetke]
+* ActiveRecord::RecordInvalid now states which validations failed in its default error message [Tobias Lütke]
-* Using AssociationCollection#build with arrays of hashes should call build, not create [DHH]
+* Using AssociationCollection#build with arrays of hashes should call build, not create [David Heinemeier Hansson]
* Remove definition of reloadable? from ActiveRecord::Base to make way for new Reloadable code. [Nicholas Seckar]
* Fixed schema handling for DB2 adapter that didn't work: an initial schema could be set, but it wasn't used when getting tables and indexes #3678 [Maik Schmidt]
-* Support the :column option for remove_index with the PostgreSQL adapter. #3661 [shugo@ruby-lang.org]
+* Support the :column option for remove_index with the PostgreSQL adapter. #3661 [Shugo Maeda]
* Add documentation for add_index and remove_index. #3600 [Manfred Stienstra <m.stienstra@fngtps.com>]
-* If the OCI library is not available, raise an exception indicating as much. #3593 [schoenm@earthlink.net]
+* If the OCI library is not available, raise an exception indicating as much. #3593 [Michael Schoen]
* Add explicit :order in finder tests as postgresql orders results differently by default. #3577. [Rick Olson]
@@ -3660,7 +3664,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Show a meaningful error when the DB2 adapter cannot be loaded due to missing dependencies. [Nicholas Seckar]
-* Make .count work for has_many associations with multi line finder sql [schoenm@earthlink.net]
+* Make .count work for has_many associations with multi line finder sql [Michael Schoen]
* Add AR::Base.base_class for querying the ancestor AR::Base subclass [Jamis Buck]
@@ -3668,13 +3672,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Don't hardcode 'id' in acts as list. [ror@philippeapril.com]
-* Fix date errors for SQLServer in association tests. #3406 [kevin.clark@gmal.com]
+* Fix date errors for SQLServer in association tests. #3406 [Kevin Clark]
* Escape database name in MySQL adapter when creating and dropping databases. #3409 [anna@wota.jp]
* Disambiguate table names for columns in validates_uniquness_of's WHERE clause. #3423 [alex.borovsky@gmail.com]
-* .with_scope imposed create parameters now bypass attr_protected [Tobias Luetke]
+* .with_scope imposed create parameters now bypass attr_protected [Tobias Lütke]
* Don't raise an exception when there are more keys than there are named bind variables when sanitizing conditions. [Marcel Molina Jr.]
@@ -3682,25 +3686,25 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Sanitize scoped conditions. [Marcel Molina Jr.]
-* Added option to Base.reflection_of_all_associations to specify a specific association to scope the call. For example Base.reflection_of_all_associations(:has_many) [DHH]
+* Added option to Base.reflection_of_all_associations to specify a specific association to scope the call. For example Base.reflection_of_all_associations(:has_many) [David Heinemeier Hansson]
-* Added ActiveRecord::SchemaDumper.ignore_tables which tells SchemaDumper which tables to ignore. Useful for tables with funky column like the ones required for tsearch2. [TobiasLuetke]
+* Added ActiveRecord::SchemaDumper.ignore_tables which tells SchemaDumper which tables to ignore. Useful for tables with funky column like the ones required for tsearch2. [Tobias Lütke]
-* SchemaDumper now doesn't fail anymore when there are unknown column types in the schema. Instead the table is ignored and a Comment is left in the schema.rb. [TobiasLuetke]
+* SchemaDumper now doesn't fail anymore when there are unknown column types in the schema. Instead the table is ignored and a Comment is left in the schema.rb. [Tobias Lütke]
* Fixed that saving a model with multiple habtm associations would only save the first one. #3244 [yanowitz-rubyonrails@quantumfoam.org, Florian Weber]
* Fix change_column to work with PostgreSQL 7.x and 8.x. #3141 [wejn@box.cz, Rick Olson, Scott Barron]
-* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Luetke]
+* removed :piggyback in favor of just allowing :select on :through associations. [Tobias Lütke]
-* made method missing delegation to class methods on relation target work on :through associations. [Tobias Luetke]
+* made method missing delegation to class methods on relation target work on :through associations. [Tobias Lütke]
-* made .find() work on :through relations. [Tobias Luetke]
+* made .find() work on :through relations. [Tobias Lütke]
* Fix typo in association docs. #3296. [Blair Zajac]
-* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Luetke]
+* Fixed :through relations when using STI inherited classes would use the inherited class's name as foreign key on the join model [Tobias Lütke]
*1.13.2* (December 13th, 2005)
@@ -3708,7 +3712,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* MySQL: allow encoding option for mysql.rb driver. [Jeremy Kemper]
-* Added option inheritance for find calls on has_and_belongs_to_many and has_many assosociations [DHH]. Example:
+* Added option inheritance for find calls on has_and_belongs_to_many and has_many assosociations [David Heinemeier Hansson]. Example:
class Post
has_many :recent_comments, :class_name => "Comment", :limit => 10, :include => :author
@@ -3718,7 +3722,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
post.recent_comments.find(:all, :limit => nil) # Uses no limit but include authors
post.recent_comments.find(:all, :limit => nil, :include => nil) # Uses no limit and doesn't include authors
-* Added option to specify :group, :limit, :offset, and :select options from find on has_and_belongs_to_many and has_many assosociations [DHH]
+* Added option to specify :group, :limit, :offset, and :select options from find on has_and_belongs_to_many and has_many assosociations [David Heinemeier Hansson]
* MySQL: fixes for the bundled mysql.rb driver. #3160 [Justin Forder]
@@ -3736,7 +3740,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* MySQL: more robust test for nullified result hashes. #3124 [Stefan Kaes]
-* Reloading an instance refreshes its aggregations as well as its associations. #3024 [François Beausolei]
+* Reloading an instance refreshes its aggregations as well as its associations. #3024 [François Beausoleil]
* Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 [Paul Hammmond]
@@ -3746,9 +3750,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat. Eliminate usage of mysql.ping because it doesn't guarantee reconnect. Explicitly close and reopen the connection instead. [Jeremy Kemper]
-* Added preliminary support for polymorphic associations [DHH]
+* Added preliminary support for polymorphic associations [David Heinemeier Hansson]
-* Added preliminary support for join models [DHH]
+* Added preliminary support for join models [David Heinemeier Hansson]
* Allow validate_uniqueness_of to be scoped by more than just one column. #1559. [jeremy@jthopple.com, Marcel Molina Jr.]
@@ -3774,7 +3778,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Add tasks to create, drop and rebuild the MySQL and PostgreSQL test databases. [Marcel Molina Jr.]
-* Correct boolean handling in generated reader methods. #2945 [don.park@gmail.com, Stefan Kaes]
+* Correct boolean handling in generated reader methods. #2945 [Don Park, Stefan Kaes]
* Don't generate read methods for columns whose names are not valid ruby method names. #2946 [Stefan Kaes]
@@ -3822,7 +3826,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fix sqlite adaptor's detection of missing dbfile or database declaration. [Nicholas Seckar]
-* Fixed acts_as_list for definitions without an explicit :order #2803 [jonathan@bluewire.net.nz]
+* Fixed acts_as_list for definitions without an explicit :order #2803 [Jonathan Viney]
* Upgrade bundled ruby-mysql 0.2.4 with mysql411 shim (see #440) to ruby-mysql 0.2.6 with a patchset for 4.1 protocol support. Local change [301] is now a part of the main driver; reapplied local change [2182]. Removed GC.start from Result.free. [tommy@tmtm.org, akuroda@gmail.com, Doug Fales <doug.fales@gmail.com>, Jeremy Kemper]
@@ -3841,7 +3845,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed faulty regex in get_table_name method (SQLServerAdapter) #2639 [Ryan Tomayko]
-* Added :include as an option for association declarations [DHH]. Example:
+* Added :include as an option for association declarations [David Heinemeier Hansson]. Example:
has_many :posts, :include => [ :author, :comments ]
@@ -3856,7 +3860,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed that SQL Server should ignore :size declarations on anything but integer and string in the agnostic schema representation #2756 [Ryan Tomayko]
-* Added constrain scoping for creates using a hash of attributes bound to the :creation key [DHH]. Example:
+* Added constrain scoping for creates using a hash of attributes bound to the :creation key [David Heinemeier Hansson]. Example:
Comment.constrain(:creation => { :post_id => 5 }) do
# Associated with :post_id
@@ -3868,7 +3872,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
# If the tag doesn't exist, a new one is created that's associated with the person
person.tags.find_or_create_by_name("Summer")
-* Added find_or_create_by_X as a second type of dynamic finder that'll create the record if it doesn't already exist [DHH]. Example:
+* Added find_or_create_by_X as a second type of dynamic finder that'll create the record if it doesn't already exist [David Heinemeier Hansson]. Example:
# No 'Summer' tag exists
Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer")
@@ -3876,7 +3880,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
# Now the 'Summer' tag does exist
Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
-* Added extension capabilities to has_many and has_and_belongs_to_many proxies [DHH]. Example:
+* Added extension capabilities to has_many and has_and_belongs_to_many proxies [David Heinemeier Hansson]. Example:
class Account < ActiveRecord::Base
has_many :people do
@@ -3895,9 +3899,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
Note that the anoymous module must be declared using brackets, not do/end (due to order of evaluation).
-* Omit internal dtproperties table from SQLServer table list. #2729 [rtomayko@gmail.com]
+* Omit internal dtproperties table from SQLServer table list. #2729 [Ryan Tomayko]
-* Quote column names in generated SQL. #2728 [rtomayko@gmail.com]
+* Quote column names in generated SQL. #2728 [Ryan Tomayko]
* Correct the pure-Ruby MySQL 4.1.1 shim's version test. #2718 [Jeremy Kemper]
@@ -3911,7 +3915,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Worked around that connection can't be reset if allow_concurrency is off. #2648 [Michael Schoen <schoenm@earthlink.net>]
-* Fixed SQL Server adapter to pass even more tests and do even better #2634 [rtomayko@gmail.com]
+* Fixed SQL Server adapter to pass even more tests and do even better #2634 [Ryan Tomayko]
* Fixed SQL Server adapter so it honors options[:conditions] when applying :limits #1978 [Tom Ward]
@@ -3924,18 +3928,18 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added :offset and :limit to the kinds of options that Base.constrain can use #2466 [duane.johnson@gmail.com]
-* Fixed handling of nil number columns on Oracle and cleaned up tests for Oracle in general #2555 [schoenm@earthlink.net]
+* Fixed handling of nil number columns on Oracle and cleaned up tests for Oracle in general #2555 [Michael Schoen]
* Added quoted_true and quoted_false methods and tables to db2_adapter and cleaned up tests for DB2 #2493, #2624 [maik schmidt]
*1.12.2* (October 26th, 2005)
-* Allow symbols to rename columns when using SQLite adapter. #2531 [kevin.clark@gmail.com]
+* Allow symbols to rename columns when using SQLite adapter. #2531 [Kevin Clark]
* Map Active Record time to SQL TIME. #2575, #2576 [Robby Russell <robby@planetargon.com>]
-* Clarify semantics of ActiveRecord::Base#respond_to? #2560 [skaes@web.de]
+* Clarify semantics of ActiveRecord::Base#respond_to? #2560 [Stefan Kaes]
* Fixed Association#clear for associations which have not yet been accessed. #2524 [Patrick Lenz <patrick@lenz.sh>]
@@ -3989,25 +3993,25 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Add test coverage for content_columns. #2432. [coffee2code]
-* Speed up for unthreaded environments. #2431. [skaes@web.de]
+* Speed up for unthreaded environments. #2431. [Stefan Kaes]
-* Optimization for Mysql selects using mysql-ruby extension greater than 2.6.3. #2426. [skaes@web.de]
+* Optimization for Mysql selects using mysql-ruby extension greater than 2.6.3. #2426. [Stefan Kaes]
-* Speed up the setting of table_name. #2428. [skaes@web.de]
+* Speed up the setting of table_name. #2428. [Stefan Kaes]
-* Optimize instantiation of STI subclass records. In partial fullfilment of #1236. [skaes@web.de]
+* Optimize instantiation of STI subclass records. In partial fullfilment of #1236. [Stefan Kaes]
* Fix typo of 'constrains' to 'contraints'. #2069. [Michael Schuerig <michael@schuerig.de>]
-* Optimization refactoring for add_limit_offset!. In partial fullfilment of #1236. [skaes@web.de]
+* Optimization refactoring for add_limit_offset!. In partial fullfilment of #1236. [Stefan Kaes]
* Add ability to get all siblings, including the current child, with acts_as_tree. Recloses #2140. [Michael Schuerig <michael@schuerig.de>]
-* Add geometric type for postgresql adapter. #2233 [akaspick@gmail.com]
+* Add geometric type for postgresql adapter. #2233 [Andrew Kaspick]
-* Add option (true by default) to generate reader methods for each attribute of a record to avoid the overhead of calling method missing. In partial fullfilment of #1236. [skaes@web.de]
+* Add option (true by default) to generate reader methods for each attribute of a record to avoid the overhead of calling method missing. In partial fullfilment of #1236. [Stefan Kaes]
-* Add convenience predicate methods on Column class. In partial fullfilment of #1236. [skaes@web.de]
+* Add convenience predicate methods on Column class. In partial fullfilment of #1236. [Stefan Kaes]
* Raise errors when invalid hash keys are passed to ActiveRecord::Base.find. #2363 [Chad Fowler <chad@chadfowler.com>, Nicholas Seckar]
@@ -4032,7 +4036,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Make update_attribute use the same writer method that update_attributes uses.
#2237 [trevor@protocool.com]
-* Make migrations honor table name prefixes and suffixes. #2298 [Jakob S, Marcel Molina]
+* Make migrations honor table name prefixes and suffixes. #2298 [Jakob Skjerning, Marcel Molina Jr.]
* Correct and optimize PostgreSQL bytea escaping. #1745, #1837 [dave@cherryville.org, ken@miriamtech.com, bellis@deepthought.org]
@@ -4040,7 +4044,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Standardize the interpretation of boolean columns in the Mysql and Sqlite adapters. (Use MysqlAdapter.emulate_booleans = false to disable this behavior)
-* Added new symbol-driven approach to activating observers with Base#observers= [DHH]. Example:
+* Added new symbol-driven approach to activating observers with Base#observers= [David Heinemeier Hansson]. Example:
ActiveRecord::Base.observers = :cacher, :garbage_collector
@@ -4056,19 +4060,19 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed that the create_x method from belongs_to wouldn't save the association properly #2042 [Florian Weber]
-* Fixed saving a record with two unsaved belongs_to associations pointing to the same object #2023 [Tobias Luetke]
+* Fixed saving a record with two unsaved belongs_to associations pointing to the same object #2023 [Tobias Lütke]
* Improved migrations' behavior when the schema_info table is empty. [Nicholas Seckar]
* Fixed that Observers didn't observe sub-classes #627 [Florian Weber]
-* Fix eager loading error messages, allow :include to specify tables using strings or symbols. Closes #2222 [Marcel Molina]
+* Fix eager loading error messages, allow :include to specify tables using strings or symbols. Closes #2222 [Marcel Molina Jr.]
* Added check for RAILS_CONNECTION_ADAPTERS on startup and only load the connection adapters specified within if its present (available in Rails through config.connection_adapters using the new config) #1958 [skae]
* Fixed various problems with has_and_belongs_to_many when using customer finder_sql #2094 [Florian Weber]
-* Added better exception error when unknown column types are used with migrations #1814 [fbeausoleil@ftml.net]
+* Added better exception error when unknown column types are used with migrations #1814 [François Beausoleil]
* Fixed "connection lost" issue with the bundled Ruby/MySQL driver (would kill the app after 8 hours of inactivity) #2163, #428 [kajism@yahoo.com]
@@ -4084,13 +4088,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Make sure the schema_info table is created before querying the current version #1903
-* Fixtures ignore table name prefix and suffix #1987 [Jakob S]
+* Fixtures ignore table name prefix and suffix #1987 [Jakob Skjerning]
-* Add documentation for index_type argument to add_index method for migrations #2005 [blaine@odeo.com]
+* Add documentation for index_type argument to add_index method for migrations #2005 [Blaine]
* Modify read_attribute to allow a symbol argument #2024 [Ken Kunz]
-* Make destroy return self #1913 [sebastian.kanthak@muehlheim.de]
+* Make destroy return self #1913 [Sebastian Kanthak]
* Fix typo in validations documentation #1938 [court3nay]
@@ -4112,7 +4116,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed the handling of multiple blob columns in Oracle if one or more of them are null #1798
-* Added support for calling constrained class methods on has_many and has_and_belongs_to_many collections #1764 [Tobias Luetke]
+* Added support for calling constrained class methods on has_many and has_and_belongs_to_many collections #1764 [Tobias Lütke]
class Comment < AR:B
def self.search(q)
@@ -4144,19 +4148,19 @@ in effect. Added :readonly finder constraint. Calling an association collectio
*1.11.1* (11 July, 2005)
-* Added support for limit and offset with eager loading of has_one and belongs_to associations. Using the options with has_many and has_and_belongs_to_many associations will now raise an ActiveRecord::ConfigurationError #1692 [Rick Olsen]
+* Added support for limit and offset with eager loading of has_one and belongs_to associations. Using the options with has_many and has_and_belongs_to_many associations will now raise an ActiveRecord::ConfigurationError #1692 [Rick Olson]
* Fixed that assume_bottom_position (in acts_as_list) could be called on items already last in the list and they would move one position away from the list #1648 [tyler@kianta.com]
* Added ActiveRecord::Base.threaded_connections flag to turn off 1-connection per thread (required for thread safety). By default it's on, but WEBrick in Rails need it off #1685 [Sam Stephenson]
-* Correct reflected table name for singular associations. #1688 [court3nay@gmail.com]
+* Correct reflected table name for singular associations. #1688 [court3nay]
* Fixed optimistic locking with SQL Server #1660 [tom@popdog.net]
* Added ActiveRecord::Migrator.migrate that can figure out whether to go up or down based on the target version and the current
-* Added better error message for "packets out of order" #1630 [courtenay]
+* Added better error message for "packets out of order" #1630 [court3nay]
* Fixed first run of "rake migrate" on PostgreSQL by not expecting a return value on the id #1640
@@ -4167,7 +4171,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Changed logging of SQL statements to use the DEBUG level instead of INFO
-* Added new Migrations framework for describing schema transformations in a way that can be easily applied across multiple databases #1604 [Tobias Luetke] See documentation under ActiveRecord::Migration and the additional support in the Rails rakefile/generator.
+* Added new Migrations framework for describing schema transformations in a way that can be easily applied across multiple databases #1604 [Tobias Lütke] See documentation under ActiveRecord::Migration and the additional support in the Rails rakefile/generator.
* Added callback hooks to association collections #1549 [Florian Weber]. Example:
@@ -4190,13 +4194,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Speed up ActiveRecord#method_missing for the common case (read_attribute).
-* Only notify observers on after_find and after_initialize if these methods are defined on the model. #1235 [skaes@web.de]
+* Only notify observers on after_find and after_initialize if these methods are defined on the model. #1235 [Stefan Kaes]
* Fixed that single-table inheritance sub-classes couldn't be used to limit the result set with eager loading #1215 [Chris McGrath]
* Fixed validates_numericality_of to work with overrided getter-method when :allow_nil is on #1316 [raidel@onemail.at]
-* Added roots, root, and siblings to the batch of methods added by acts_as_tree #1541 [michael@schuerig.de]
+* Added roots, root, and siblings to the batch of methods added by acts_as_tree #1541 [Michael Schuerig]
* Added support for limit/offset with the MS SQL Server driver so that pagination will now work #1569 [DeLynn Berry]
@@ -4218,7 +4222,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
:conditions => 'project_id=1'
)
-* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina]
+* Fixed that validations didn't respecting custom setting for too_short, too_long messages #1437 [Marcel Molina Jr.]
* Fixed that clear_association_cache doesn't delete new associations on new records (so you can safely place new records in the session with Action Pack without having new associations wiped) #1494 [cluon]
@@ -4245,7 +4249,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Corrected typo in find SQL for has_and_belongs_to_many. #1312 [ben@bensinclair.com]
-* Fixed sanitized conditions for has_many finder method. #1281 [jackc@hylesanderson.com, pragdave, Tobias Luetke]
+* Fixed sanitized conditions for has_many finder method. #1281 [jackc@hylesanderson.com, pragdave, Tobias Lütke]
* Comprehensive PostgreSQL schema support. Use the optional schema_search_path directive in database.yml to give a comma-separated list of schemas to search for your tables. This allows you, for example, to have tables in a shared schema without having to use a custom table name. See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html to learn more. #827 [dave@cherryville.org]
@@ -4303,15 +4307,15 @@ in effect. Added :readonly finder constraint. Calling an association collectio
# SELECT * FROM topics WHERE title IN ('First', 'Second')
Topic.find_all_by_title(["First", "Second"])
-* Added compatibility with camelCase column names for dynamic finders #533 [Dee.Zsombor]
+* Added compatibility with camelCase column names for dynamic finders #533 [Dee Zsombor]
-* Fixed extraneous comma in count() function that made it not work with joins #1156 [jarkko/Dee.Zsombor]
+* Fixed extraneous comma in count() function that made it not work with joins #1156 [Jarkko Laine/Dee Zsombor]
* Fixed incompatibility with Base#find with an array of ids that would fail when using eager loading #1186 [Alisdair McDiarmid]
* Fixed that validate_length_of lost :on option when :within was specified #1195 [jhosteny@mac.com]
-* Added encoding and min_messages options for PostgreSQL #1205 [shugo]. Configuration example:
+* Added encoding and min_messages options for PostgreSQL #1205 [Shugo Maeda]. Configuration example:
development:
adapter: postgresql
@@ -4369,7 +4373,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
a single query. A good use case for this is a threaded post system, where you want
to display every reply to a comment without multiple selects.
-* Added Base.save! that attempts to save the record just like Base.save but will raise a RecordInvalid exception instead of returning false if the record is not valid [After much pestering from Dave Thomas]
+* Added Base.save! that attempts to save the record just like Base.save but will raise a RecordInvalid exception instead of returning false if the record is not valid [Dave Thomas]
* Fixed PostgreSQL usage of fixtures with regards to public schemas and table names with dots #962 [gnuman1@gmail.com]
@@ -4399,7 +4403,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed counter_sql when no records exist in database for PostgreSQL (would give error, not 0) #1039 [Caleb Tennis]
-* Fixed that benchmarking times for rendering included db runtimes #987 [skaes@web.de]
+* Fixed that benchmarking times for rendering included db runtimes #987 [Stefan Kaes]
* Fixed boolean queries for t/f fields in PostgreSQL #995 [dave@cherryville.org]
@@ -4407,9 +4411,9 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed auto-stamping of dates (created_on/updated_on) for PostgreSQL #985 [dave@cherryville.org]
-* Fixed Base.silence/benchmark to only log if a logger has been configured #986 [skaes@web.de]
+* Fixed Base.silence/benchmark to only log if a logger has been configured #986 [Stefan Kaes]
-* Added a join parameter as the third argument to Base.find_first and as the second to Base.count #426, #988 [skaes@web.de]
+* Added a join parameter as the third argument to Base.find_first and as the second to Base.count #426, #988 [Stefan Kaes]
* Fixed bug in Base#hash method that would treat records with the same string-based id as different [Dave Thomas]
@@ -4495,7 +4499,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
account.save # => CC with id = 12 is destroyed
-* Added validates_numericality_of #716 [skanthak/c.r.mcgrath]. Docuemntation:
+* Added validates_numericality_of #716 [Sebastian Kanthak/Chris McGrath]. Docuemntation:
Validates whether the value of the specified attribute is numeric by trying to convert it to
a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression
@@ -4565,7 +4569,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
4. Added logic to the simplified_type method that allows the database to specify the scale of float data.
5. Adjusted the quote_column_name to account for the fact that MS SQL is bothered by a forward slash in the data string.
-* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow@yahoo.com]
+* Fixed that the dynamic finder like find_all_by_something_boolean(false) didn't work #649 [lmarlow]
* Added validates_each that validates each specified attribute against a block #610 [Jeremy Kemper]. Example:
@@ -4777,7 +4781,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
After:
before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
-* Added :counter_cache option to acts_as_tree that works just like the one you can define on belongs_to #371 [Josh]
+* Added :counter_cache option to acts_as_tree that works just like the one you can define on belongs_to #371 [Josh Peek]
* Added Base.default_timezone accessor that determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates
and times from the database. This is set to :local by default.
@@ -4796,7 +4800,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed handling of binary content in blobs and similar fields for Ruby/MySQL and SQLite #409 [xal]
-* Fixed a bug in the Ruby/MySQL that caused binary content to be escaped badly and come back mangled #405 [Tobias Luetke]
+* Fixed a bug in the Ruby/MySQL that caused binary content to be escaped badly and come back mangled #405 [Tobias Lütke]
* Fixed that the const_missing autoload assumes the requested constant is set by require_association and calls const_get to retrieve it.
If require_association did not set the constant then const_get will call const_missing, resulting in an infinite loop #380 [Jeremy Kemper]
@@ -4837,7 +4841,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added work-around for PostgreSQL and the problem of getting fixtures to be created from id 1 on each test case.
This only works for auto-incrementing primary keys called "id" for now #359 [Scott Baron]
-* Added Base#clear_association_cache to empty all the cached associations #347 [Tobias Luetke]
+* Added Base#clear_association_cache to empty all the cached associations #347 [Tobias Lütke]
* Added more informative exceptions in establish_connection #356 [Jeremy Kemper]
@@ -4854,7 +4858,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Added that query benchmarking will only happen if its going to be logged anyway #344
-* Added higher_item and lower_item as public methods for acts_as_list #342 [Tobias Luetke]
+* Added higher_item and lower_item as public methods for acts_as_list #342 [Tobias Lütke]
* Fixed that options[:counter_sql] was overwritten with interpolated sql rather than original sql #355 [Jeremy Kemper]
@@ -4885,7 +4889,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
validates_inclusion_of :age, :in=>0..99
end
-* Added acts_as_list that can decorates an existing class with methods like move_higher/lower, move_to_top/bottom. [Tobias Luetke] Example:
+* Added acts_as_list that can decorates an existing class with methods like move_higher/lower, move_to_top/bottom. [Tobias Lütke] Example:
class TodoItem < ActiveRecord::Base
acts_as_list :scope => :todo_list_id
@@ -4893,12 +4897,12 @@ in effect. Added :readonly finder constraint. Calling an association collectio
end
* Added acts_as_tree that can decorates an existing class with a many to many relationship with itself. Perfect for categories in
- categories and the likes. [Tobias Luetke]
+ categories and the likes. [Tobias Lütke]
* Added that Active Records will automatically record creation and/or update timestamps of database objects if fields of the names
- created_at/created_on or updated_at/updated_on are present. [Tobias Luetke]
+ created_at/created_on or updated_at/updated_on are present. [Tobias Lütke]
-* Added Base.default_error_messages as a hash of all the error messages used in the validates_*_of so they can be changed in one place [Tobias Luetke]
+* Added Base.default_error_messages as a hash of all the error messages used in the validates_*_of so they can be changed in one place [Tobias Lütke]
* Added automatic transaction block around AssociationCollection.<<, AssociationCollection.delete, and AssociationCollection.destroy_all
@@ -4931,13 +4935,13 @@ in effect. Added :readonly finder constraint. Calling an association collectio
errors.on("name") # => "must be shorter"
* Added Base.validates_format_of that Validates whether the value of the specified attribute is of the correct form by matching
- it against the regular expression provided. [Marcel]
+ it against the regular expression provided. [Marcel Molina Jr.]
class Person < ActiveRecord::Base
validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, :on => :create
end
-* Added Base.validates_length_of that delegates to add_on_boundary_breaking #312 [Tobias Luetke]. Example:
+* Added Base.validates_length_of that delegates to add_on_boundary_breaking #312 [Tobias Lütke]. Example:
Validates that the specified attribute matches the length restrictions supplied in either:
@@ -5156,7 +5160,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed that calling id would create the instance variable for new_records preventing them from being saved correctly [Jeremy Kemper]
-* Added sanitization feature to HasManyAssociation#find_all so it works just like Base.find_all [Sam Stephenson/bitsweat]
+* Added sanitization feature to HasManyAssociation#find_all so it works just like Base.find_all [Sam Stephenson/Jeremy Kemper]
* Added that you can pass overlapping ids to find without getting duplicated records back [Jeremy Kemper]
@@ -5166,7 +5170,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Fixed that quotes would break regular non-yaml fixtures [Dmitry Sabanin/daft]
-* Fixed fixtures on windows with line endings cause problems under unix / mac [Tobias Luetke]
+* Fixed fixtures on windows with line endings cause problems under unix / mac [Tobias Lütke]
* Added HasAndBelongsToManyAssociation#find(id) that'll search inside the collection and find the object or record with that id
@@ -5287,7 +5291,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
project.milestones << Milestone.find_all
end
-* Added logging of invalid SQL statements [Suggested by Daniel Von Fange]
+* Added logging of invalid SQL statements [Daniel Von Fange]
* Added alias Errors#[] for Errors#on, so you can now say person.errors["name"] to retrieve the errors for name [Andreas Schwarz]
@@ -5319,7 +5323,7 @@ in effect. Added :readonly finder constraint. Calling an association collectio
* Changed class inheritable attributes to not use eval [Caio Chassot]
-* Changed Errors#add to now use "invalid" as the default message instead of true, which means full_messages work with those [Marcel Molina Jr]
+* Changed Errors#add to now use "invalid" as the default message instead of true, which means full_messages work with those [Marcel Molina Jr.]
* Fixed spelling on Base#add_on_boundry_breaking to Base#add_on_boundary_breaking (old naming still works) [Marcel Molina Jr.]
@@ -5771,7 +5775,7 @@ _Misc_
*0.8.1*
-* Added object-level transactions [Thanks to Austin Ziegler for Transaction::Simple]
+* Added object-level transactions [Austin Ziegler]
* Changed adapter-specific connection methods to use centralized ActiveRecord::Base.establish_connection,
which is parametized through a config hash with symbol keys instead of a regular parameter list.
@@ -5806,4 +5810,4 @@ _Misc_
*0.7.6*
* Fixed the install.rb to create the lib/active_record/support directory [Spotted by Gavin Sinclair]
-* Fixed that has_association? would always return true [Spotted by Daniel Von Fange]
+* Fixed that has_association? would always return true [Daniel Von Fange]
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 348e5b94af..390c005785 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -37,6 +37,8 @@ module ActiveRecord
[Base, DynamicFinderMatch, ConnectionAdapters::AbstractAdapter]
end
+ autoload :VERSION, 'active_record/version'
+
autoload :ActiveRecordError, 'active_record/base'
autoload :ConnectionNotEstablished, 'active_record/base'
@@ -49,6 +51,7 @@ module ActiveRecord
autoload :Callbacks, 'active_record/callbacks'
autoload :Dirty, 'active_record/dirty'
autoload :DynamicFinderMatch, 'active_record/dynamic_finder_match'
+ autoload :DynamicScopeMatch, 'active_record/dynamic_scope_match'
autoload :Migration, 'active_record/migration'
autoload :Migrator, 'active_record/migration'
autoload :NamedScope, 'active_record/named_scope'
@@ -58,6 +61,7 @@ module ActiveRecord
autoload :Schema, 'active_record/schema'
autoload :SchemaDumper, 'active_record/schema_dumper'
autoload :Serialization, 'active_record/serialization'
+ autoload :SessionStore, 'active_record/session_store'
autoload :TestCase, 'active_record/test_case'
autoload :Timestamp, 'active_record/timestamp'
autoload :Transactions, 'active_record/transactions'
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index 99c3ce5e62..e4ab69aa1b 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -43,7 +43,7 @@ module ActiveRecord
# loading in a more high-level (application developer-friendly) manner.
module ClassMethods
protected
-
+
# Eager loads the named associations for the given ActiveRecord record(s).
#
# In this description, 'association name' shall refer to the name passed
@@ -94,8 +94,8 @@ module ActiveRecord
raise "parent must be an association name" unless parent.is_a?(String) || parent.is_a?(Symbol)
preload_associations(records, parent, preload_options)
reflection = reflections[parent]
- parents = records.map {|record| record.send(reflection.name)}.flatten
- unless parents.empty? || parents.first.nil?
+ parents = records.map {|record| record.send(reflection.name)}.flatten.compact
+ unless parents.empty?
parents.first.class.preload_associations(parents, child)
end
end
@@ -113,7 +113,7 @@ module ActiveRecord
# unnecessarily
records.group_by {|record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, records|
raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection
-
+
# 'reflection.macro' can return 'belongs_to', 'has_many', etc. Thus,
# the following could call 'preload_belongs_to_association',
# 'preload_has_many_association', etc.
@@ -128,7 +128,7 @@ module ActiveRecord
association_proxy.target.push(*[associated_record].flatten)
end
end
-
+
def add_preloaded_record_to_collection(parent_records, reflection_name, associated_record)
parent_records.each do |parent_record|
parent_record.send("set_#{reflection_name}_target", associated_record)
@@ -183,18 +183,19 @@ module ActiveRecord
conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}"
conditions << append_conditions(reflection, preload_options)
- associated_records = reflection.klass.find(:all, :conditions => [conditions, ids],
- :include => options[:include],
- :joins => "INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}",
- :select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id",
- :order => options[:order])
-
+ associated_records = reflection.klass.with_exclusive_scope do
+ reflection.klass.find(:all, :conditions => [conditions, ids],
+ :include => options[:include],
+ :joins => "INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}",
+ :select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id",
+ :order => options[:order])
+ end
set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'the_parent_record_id')
end
def preload_has_one_association(records, reflection, preload_options={})
return if records.first.send("loaded_#{reflection.name}?")
- id_to_record_map, ids = construct_id_map(records)
+ id_to_record_map, ids = construct_id_map(records, reflection.options[:primary_key])
options = reflection.options
records.each {|record| record.send("set_#{reflection.name}_target", nil)}
if options[:through]
@@ -204,9 +205,18 @@ module ActiveRecord
unless through_records.empty?
source = reflection.source_reflection.name
through_records.first.class.preload_associations(through_records, source)
- through_records.each do |through_record|
- add_preloaded_record_to_collection(id_to_record_map[through_record[through_primary_key].to_s],
- reflection.name, through_record.send(source))
+ if through_reflection.macro == :belongs_to
+ rev_id_to_record_map, rev_ids = construct_id_map(records, through_primary_key)
+ rev_primary_key = through_reflection.klass.primary_key
+ through_records.each do |through_record|
+ add_preloaded_record_to_collection(rev_id_to_record_map[through_record[rev_primary_key].to_s],
+ reflection.name, through_record.send(source))
+ end
+ else
+ through_records.each do |through_record|
+ add_preloaded_record_to_collection(id_to_record_map[through_record[through_primary_key].to_s],
+ reflection.name, through_record.send(source))
+ end
end
end
else
@@ -219,7 +229,7 @@ module ActiveRecord
options = reflection.options
primary_key_name = reflection.through_reflection_primary_key_name
- id_to_record_map, ids = construct_id_map(records, primary_key_name)
+ id_to_record_map, ids = construct_id_map(records, primary_key_name || reflection.options[:primary_key])
records.each {|record| record.send(reflection.name).loaded}
if options[:through]
@@ -239,7 +249,7 @@ module ActiveRecord
reflection.primary_key_name)
end
end
-
+
def preload_through_records(records, reflection, through_association)
through_reflection = reflections[through_association]
through_primary_key = through_reflection.primary_key_name
@@ -307,6 +317,7 @@ module ActiveRecord
klasses_and_ids.each do |klass_and_id|
klass_name, id_map = *klass_and_id
+ next if id_map.empty?
klass = klass_name.constantize
table_name = klass.quoted_table_name
@@ -323,11 +334,13 @@ module ActiveRecord
end
conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} #{in_or_equals_for_ids(ids)}"
conditions << append_conditions(reflection, preload_options)
- associated_records = klass.find(:all, :conditions => [conditions, ids],
+ associated_records = klass.with_exclusive_scope do
+ klass.find(:all, :conditions => [conditions, ids],
:include => options[:include],
:select => options[:select],
:joins => options[:joins],
:order => options[:order])
+ end
set_association_single_records(id_map, reflection.name, associated_records, primary_key)
end
end
@@ -345,13 +358,15 @@ module ActiveRecord
conditions << append_conditions(reflection, preload_options)
- reflection.klass.find(:all,
+ reflection.klass.with_exclusive_scope do
+ reflection.klass.find(:all,
:select => (preload_options[:select] || options[:select] || "#{table_name}.*"),
:include => preload_options[:include] || options[:include],
:conditions => [conditions, ids],
:joins => options[:joins],
:group => preload_options[:group] || options[:group],
:order => preload_options[:order] || options[:order])
+ end
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 3fbbea43ed..86616abf52 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -22,7 +22,7 @@ module ActiveRecord
through_reflection = reflection.through_reflection
source_reflection_names = reflection.source_reflection_names
source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
- super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence :connector => 'or'} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence :connector => 'or'}?")
+ super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence :two_words_connector => ' or ', :last_word_connector => ', or '} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence :two_words_connector => ' or ', :last_word_connector => ', or '}?")
end
end
@@ -153,7 +153,7 @@ module ActiveRecord
# #others.destroy_all | X | X | X
# #others.find(*args) | X | X | X
# #others.find_first | X | |
- # #others.exist? | X | X | X
+ # #others.exists? | X | X | X
# #others.uniq | X | X | X
# #others.reset | X | X | X
#
@@ -652,7 +652,7 @@ module ActiveRecord
# Returns the number of associated objects.
# [collection.find(...)]
# Finds an associated object according to the same rules as ActiveRecord::Base.find.
- # [collection.exist?(...)]
+ # [collection.exists?(...)]
# Checks whether an associated object with the given conditions exists.
# Uses the same rules as ActiveRecord::Base.exists?.
# [collection.build(attributes = {}, ...)]
@@ -682,7 +682,7 @@ module ActiveRecord
# * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
# * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
# * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, :conditions => "firm_id = #{id}")</tt>)
- # * <tt>Firm#clients.exist?(:name => 'ACME')</tt> (similar to <tt>Client.exist?(:name => 'ACME', :firm_id => firm.id)</tt>)
+ # * <tt>Firm#clients.exists?(:name => 'ACME')</tt> (similar to <tt>Client.exists?(:name => 'ACME', :firm_id => firm.id)</tt>)
# * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
# * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
# The declaration can also include an options hash to specialize the behavior of the association.
@@ -1107,7 +1107,7 @@ module ActiveRecord
# Finds an associated object responding to the +id+ and that
# meets the condition that it has to be associated with this object.
# Uses the same rules as ActiveRecord::Base.find.
- # [collection.exist?(...)]
+ # [collection.exists?(...)]
# Checks whether an associated object with the given conditions exists.
# Uses the same rules as ActiveRecord::Base.exists?.
# [collection.build(attributes = {})]
@@ -1133,7 +1133,7 @@ module ActiveRecord
# * <tt>Developer#projects.empty?</tt>
# * <tt>Developer#projects.size</tt>
# * <tt>Developer#projects.find(id)</tt>
- # * <tt>Developer#clients.exist?(...)</tt>
+ # * <tt>Developer#clients.exists?(...)</tt>
# * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("project_id" => id)</tt>)
# * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("project_id" => id); c.save; c</tt>)
# The declaration may include an options hash to specialize the behavior of the association.
@@ -1216,11 +1216,11 @@ module ActiveRecord
# callbacks will be executed after the association is wiped out.
old_method = "destroy_without_habtm_shim_for_#{reflection.name}"
class_eval <<-end_eval unless method_defined?(old_method)
- alias_method :#{old_method}, :destroy_without_callbacks
- def destroy_without_callbacks
- #{reflection.name}.clear
- #{old_method}
- end
+ alias_method :#{old_method}, :destroy_without_callbacks # alias_method :destroy_without_habtm_shim_for_posts, :destroy_without_callbacks
+ def destroy_without_callbacks # def destroy_without_callbacks
+ #{reflection.name}.clear # posts.clear
+ #{old_method} # destroy_without_habtm_shim_for_posts
+ end # end
end_eval
add_association_callbacks(reflection.name, options)
@@ -1453,7 +1453,7 @@ module ActiveRecord
dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions]
dependent_conditions << extra_conditions if extra_conditions
dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
-
+ dependent_conditions = dependent_conditions.gsub('@', '\@')
case reflection.options[:dependent]
when :destroy
method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym
@@ -1463,22 +1463,22 @@ module ActiveRecord
before_destroy method_name
when :delete_all
module_eval %Q{
- before_destroy do |record|
- delete_all_has_many_dependencies(record,
- "#{reflection.name}",
- #{reflection.class_name},
- "#{dependent_conditions}")
- end
+ before_destroy do |record| # before_destroy do |record|
+ delete_all_has_many_dependencies(record, # delete_all_has_many_dependencies(record,
+ "#{reflection.name}", # "posts",
+ #{reflection.class_name}, # Post,
+ %@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...)
+ end # end
}
when :nullify
module_eval %Q{
- before_destroy do |record|
- nullify_has_many_dependencies(record,
- "#{reflection.name}",
- #{reflection.class_name},
- "#{reflection.primary_key_name}",
- "#{dependent_conditions}")
- end
+ before_destroy do |record| # before_destroy do |record|
+ nullify_has_many_dependencies(record, # nullify_has_many_dependencies(record,
+ "#{reflection.name}", # "posts",
+ #{reflection.class_name}, # Post,
+ "#{reflection.primary_key_name}", # "user_id",
+ %@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...)
+ end # end
}
else
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
@@ -1731,6 +1731,11 @@ module ActiveRecord
return sanitize_sql(sql)
end
+ def tables_in_string(string)
+ return [] if string.blank?
+ string.scan(/([\.a-zA-Z_]+).?\./).flatten
+ end
+
def conditions_tables(options)
# look in both sets of conditions
conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
@@ -1741,37 +1746,55 @@ module ActiveRecord
else all << cond
end
end
- conditions.join(' ').scan(/([\.a-zA-Z_]+).?\./).flatten
+ tables_in_string(conditions.join(' '))
end
def order_tables(options)
order = [options[:order], scope(:find, :order) ].join(", ")
return [] unless order && order.is_a?(String)
- order.scan(/([\.a-zA-Z_]+).?\./).flatten
+ tables_in_string(order)
end
def selects_tables(options)
select = options[:select]
return [] unless select && select.is_a?(String)
- select.scan(/"?([\.a-zA-Z_]+)"?.?\./).flatten
+ tables_in_string(select)
+ end
+
+ def joined_tables(options)
+ scope = scope(:find)
+ joins = options[:joins]
+ merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins])
+ [table_name] + case merged_joins
+ when Symbol, Hash, Array
+ if array_of_strings?(merged_joins)
+ tables_in_string(merged_joins.join(' '))
+ else
+ join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil)
+ join_dependency.join_associations.collect {|join_association| [join_association.aliased_join_table_name, join_association.aliased_table_name]}.flatten.compact
+ end
+ else
+ tables_in_string(merged_joins)
+ end
end
# Checks if the conditions reference a table other than the current model table
- def include_eager_conditions?(options, tables = nil)
- ((tables || conditions_tables(options)) - [table_name]).any?
+ def include_eager_conditions?(options, tables = nil, joined_tables = nil)
+ ((tables || conditions_tables(options)) - (joined_tables || joined_tables(options))).any?
end
# Checks if the query order references a table other than the current model's table.
- def include_eager_order?(options, tables = nil)
- ((tables || order_tables(options)) - [table_name]).any?
+ def include_eager_order?(options, tables = nil, joined_tables = nil)
+ ((tables || order_tables(options)) - (joined_tables || joined_tables(options))).any?
end
- def include_eager_select?(options)
- (selects_tables(options) - [table_name]).any?
+ def include_eager_select?(options, joined_tables = nil)
+ (selects_tables(options) - (joined_tables || joined_tables(options))).any?
end
def references_eager_loaded_tables?(options)
- include_eager_order?(options) || include_eager_conditions?(options) || include_eager_select?(options)
+ joined_tables = joined_tables(options)
+ include_eager_order?(options, nil, joined_tables) || include_eager_conditions?(options, nil, joined_tables) || include_eager_select?(options, joined_tables)
end
def using_limitable_reflections?(reflections)
@@ -2148,7 +2171,7 @@ module ActiveRecord
aliased_table_name,
foreign_key,
parent.aliased_table_name,
- parent.primary_key
+ reflection.options[:primary_key] || parent.primary_key
]
end
when :belongs_to
@@ -2175,7 +2198,7 @@ module ActiveRecord
protected
def aliased_table_name_for(name, suffix = nil)
- if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{name.downcase}\son}
+ if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{active_record.connection.quote_table_name name.downcase}\son}
@join_dependency.table_aliases[name] += 1
end
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index 0ff91fbdf8..0fefec1216 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -83,7 +83,11 @@ module ActiveRecord
def to_ary
load_target
- @target.to_ary
+ if @target.is_a?(Array)
+ @target.to_ary
+ else
+ Array(@target)
+ end
end
def reset
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index 59f1d3b867..676c4ace61 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -180,7 +180,10 @@ module ActiveRecord
record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
else
- record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
+ unless @owner.new_record?
+ primary_key = @reflection.options[:primary_key] || :id
+ record[@reflection.primary_key_name] = @owner.send(primary_key)
+ end
end
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index a0bb3a45b0..2eeeb28d1f 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -160,9 +160,9 @@ module ActiveRecord
end
"INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [
- @reflection.through_reflection.table_name,
- @reflection.table_name, reflection_primary_key,
- @reflection.through_reflection.table_name, source_primary_key,
+ @reflection.through_reflection.quoted_table_name,
+ @reflection.quoted_table_name, reflection_primary_key,
+ @reflection.through_reflection.quoted_table_name, source_primary_key,
polymorphic_join
]
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 5d614442c3..cca012ed55 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -811,8 +811,7 @@ module ActiveRecord #:nodoc:
#
# ==== Parameters
#
- # * +updates+ - A string of column and value pairs that will be set on any records that match conditions.
- # What goes into the SET clause.
+ # * +updates+ - A string of column and value pairs that will be set on any records that match conditions. This creates the SET clause of the generated SQL.
# * +conditions+ - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro for more info.
# * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
#
@@ -1417,8 +1416,8 @@ module ActiveRecord #:nodoc:
def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
if logger && logger.level <= log_level
result = nil
- seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield }
- logger.add(log_level, "#{title} (#{'%.1f' % (seconds * 1000)}ms)")
+ ms = Benchmark.ms { result = use_silence ? silence { yield } : yield }
+ logger.add(log_level, '%s (%.1fms)' % [title, ms])
result
else
yield
@@ -1457,7 +1456,10 @@ module ActiveRecord #:nodoc:
def respond_to?(method_id, include_private = false)
if match = DynamicFinderMatch.match(method_id)
return true if all_attributes_exists?(match.attribute_names)
+ elsif match = DynamicScopeMatch.match(method_id)
+ return true if all_attributes_exists?(match.attribute_names)
end
+
super
end
@@ -1495,11 +1497,16 @@ module ActiveRecord #:nodoc:
end
if scoped?(:find, :order)
- scoped_order = reverse_sql_order(scope(:find, :order))
- scoped_methods.select { |s| s[:find].update(:order => scoped_order) }
+ scope = scope(:find)
+ original_scoped_order = scope[:order]
+ scope[:order] = reverse_sql_order(original_scoped_order)
end
- find_initial(options.merge({ :order => order }))
+ begin
+ find_initial(options.merge({ :order => order }))
+ ensure
+ scope[:order] = original_scoped_order if original_scoped_order
+ end
end
def reverse_sql_order(order_query)
@@ -1805,7 +1812,11 @@ module ActiveRecord #:nodoc:
# This also enables you to initialize a record if it is not found, such as find_or_initialize_by_amount(amount)
# or find_or_create_by_user_and_password(user, password).
#
- # Each dynamic finder or initializer/creator is also defined in the class after it is first invoked, so that future
+ # Also enables dynamic scopes like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that
+ # are turned into scoped(:conditions => ["user_name = ?", user_name]) and scoped(:conditions => ["user_name = ? AND password = ?", user_name, password])
+ # respectively.
+ #
+ # Each dynamic finder, scope or initializer/creator is also defined in the class after it is first invoked, so that future
# attempts to use it do not run through method_missing.
def method_missing(method_id, *arguments, &block)
if match = DynamicFinderMatch.match(method_id)
@@ -1814,10 +1825,31 @@ module ActiveRecord #:nodoc:
if match.finder?
finder = match.finder
bang = match.bang?
+ # def self.find_by_login_and_activated(*args)
+ # options = args.extract_options!
+ # attributes = construct_attributes_from_arguments(
+ # [:login,:activated],
+ # args
+ # )
+ # finder_options = { :conditions => attributes }
+ # validate_find_options(options)
+ # set_readonly_option!(options)
+ #
+ # if options[:conditions]
+ # with_scope(:find => finder_options) do
+ # find(:first, options)
+ # end
+ # else
+ # find(:first, options.merge(finder_options))
+ # end
+ # end
self.class_eval %{
def self.#{method_id}(*args)
options = args.extract_options!
- attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args)
+ attributes = construct_attributes_from_arguments(
+ [:#{attribute_names.join(',:')}],
+ args
+ )
finder_options = { :conditions => attributes }
validate_find_options(options)
set_readonly_option!(options)
@@ -1829,12 +1861,37 @@ module ActiveRecord #:nodoc:
else
find(:#{finder}, options.merge(finder_options))
end
- #{'result || raise(RecordNotFound)' if bang}
+ #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(\', \')}")' if bang}
end
}, __FILE__, __LINE__
send(method_id, *arguments)
elsif match.instantiator?
instantiator = match.instantiator
+ # def self.find_or_create_by_user_id(*args)
+ # guard_protected_attributes = false
+ #
+ # if args[0].is_a?(Hash)
+ # guard_protected_attributes = true
+ # attributes = args[0].with_indifferent_access
+ # find_attributes = attributes.slice(*[:user_id])
+ # else
+ # find_attributes = attributes = construct_attributes_from_arguments([:user_id], args)
+ # end
+ #
+ # options = { :conditions => find_attributes }
+ # set_readonly_option!(options)
+ #
+ # record = find(:first, options)
+ #
+ # if record.nil?
+ # record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) }
+ # yield(record) if block_given?
+ # record.save
+ # record
+ # else
+ # record
+ # end
+ # end
self.class_eval %{
def self.#{method_id}(*args)
guard_protected_attributes = false
@@ -1864,6 +1921,22 @@ module ActiveRecord #:nodoc:
}, __FILE__, __LINE__
send(method_id, *arguments, &block)
end
+ elsif match = DynamicScopeMatch.match(method_id)
+ attribute_names = match.attribute_names
+ super unless all_attributes_exists?(attribute_names)
+ if match.scope?
+ self.class_eval %{
+ def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
+ options = args.extract_options! # options = args.extract_options!
+ attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments(
+ [:#{attribute_names.join(',:')}], args # [:user_name, :password], args
+ ) # )
+ #
+ scoped(:conditions => attributes) # scoped(:conditions => attributes)
+ end # end
+ }, __FILE__, __LINE__
+ send(method_id, *arguments)
+ end
else
super
end
@@ -2052,10 +2125,10 @@ module ActiveRecord #:nodoc:
end
# Sets the default options for the model. The format of the
- # <tt>method_scoping</tt> argument is the same as in with_scope.
+ # <tt>options</tt> argument is the same as in find.
#
# class Person < ActiveRecord::Base
- # default_scope :find => { :order => 'last_name, first_name' }
+ # default_scope :order => 'last_name, first_name'
# end
def default_scope(options = {})
self.default_scoping << { :find => options, :create => (options.is_a?(Hash) && options.has_key?(:conditions)) ? options[:conditions] : {} }
@@ -2402,9 +2475,9 @@ module ActiveRecord #:nodoc:
write_attribute(self.class.primary_key, value)
end
- # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet.
+ # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet; otherwise, returns false.
def new_record?
- defined?(@new_record) && @new_record
+ @new_record || false
end
# :call-seq:
@@ -3011,7 +3084,7 @@ module ActiveRecord #:nodoc:
end
Base.class_eval do
- extend QueryCache
+ extend QueryCache::ClassMethods
include Validations
include Locking::Optimistic, Locking::Pessimistic
include AttributeMethods
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
index ccb79f547a..bbc290f721 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
@@ -123,6 +123,7 @@ module ActiveRecord
connection_handler.retrieve_connection(self)
end
+ # Returns true if +ActiveRecord+ is connected.
def connected?
connection_handler.connected?(self)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 950bd72101..00c71090f3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -14,12 +14,12 @@ module ActiveRecord
def dirties_query_cache(base, *method_names)
method_names.each do |method_name|
base.class_eval <<-end_code, __FILE__, __LINE__
- def #{method_name}_with_query_dirty(*args)
- clear_query_cache if @query_cache_enabled
- #{method_name}_without_query_dirty(*args)
- end
-
- alias_method_chain :#{method_name}, :query_dirty
+ def #{method_name}_with_query_dirty(*args) # def update_with_query_dirty(*args)
+ clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
+ #{method_name}_without_query_dirty(*args) # update_without_query_dirty(*args)
+ end # end
+ #
+ alias_method_chain :#{method_name}, :query_dirty # alias_method_chain :update, :query_dirty
end_code
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 58992f91da..273f823e7f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -32,10 +32,12 @@ module ActiveRecord
@primary = nil
end
+ # Returns +true+ if the column is either of type string or text.
def text?
type == :string || type == :text
end
+ # Returns +true+ if the column is either of type integer, float or decimal.
def number?
type == :integer || type == :float || type == :decimal
end
@@ -295,7 +297,7 @@ module ActiveRecord
# puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
# end
# end
- #
+ #
# def self.down
# ...
# end
@@ -474,12 +476,12 @@ module ActiveRecord
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
class_eval <<-EOV
- def #{column_type}(*args)
- options = args.extract_options!
- column_names = args
-
- column_names.each { |name| column(name, '#{column_type}', options) }
- end
+ def #{column_type}(*args) # def string(*args)
+ options = args.extract_options! # options = args.extract_options!
+ column_names = args # column_names = args
+ #
+ column_names.each { |name| column(name, '#{column_type}', options) } # column_names.each { |name| column(name, 'string', options) }
+ end # end
EOV
end
@@ -674,24 +676,24 @@ module ActiveRecord
# t.string(:goat, :sheep)
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
class_eval <<-EOV
- def #{column_type}(*args)
- options = args.extract_options!
- column_names = args
-
- column_names.each do |name|
- column = ColumnDefinition.new(@base, name, '#{column_type}')
- if options[:limit]
- column.limit = options[:limit]
- elsif native['#{column_type}'.to_sym].is_a?(Hash)
- column.limit = native['#{column_type}'.to_sym][:limit]
- end
- column.precision = options[:precision]
- column.scale = options[:scale]
- column.default = options[:default]
- column.null = options[:null]
- @base.add_column(@table_name, name, column.sql_type, options)
- end
- end
+ def #{column_type}(*args) # def string(*args)
+ options = args.extract_options! # options = args.extract_options!
+ column_names = args # column_names = args
+ #
+ column_names.each do |name| # column_names.each do |name|
+ column = ColumnDefinition.new(@base, name, '#{column_type}') # column = ColumnDefinition.new(@base, name, 'string')
+ if options[:limit] # if options[:limit]
+ column.limit = options[:limit] # column.limit = options[:limit]
+ elsif native['#{column_type}'.to_sym].is_a?(Hash) # elsif native['string'.to_sym].is_a?(Hash)
+ column.limit = native['#{column_type}'.to_sym][:limit] # column.limit = native['string'.to_sym][:limit]
+ end # end
+ column.precision = options[:precision] # column.precision = options[:precision]
+ column.scale = options[:scale] # column.scale = options[:scale]
+ column.default = options[:default] # column.default = options[:default]
+ column.null = options[:null] # column.null = options[:null]
+ @base.add_column(@table_name, name, column.sql_type, options) # @base.add_column(@table_name, name, column.sql_type, options)
+ end # end
+ end # end
EOV
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index b26185580c..5137b0f78c 100755
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -184,9 +184,9 @@ module ActiveRecord
# for more information about the effect of this option.
attr_accessor :transactional_fixtures
- def log_info(sql, name, seconds)
+ def log_info(sql, name, ms)
if @logger && @logger.debug?
- name = "#{name.nil? ? "SQL" : name} (#{sprintf("%.1f", seconds * 1000)}ms)"
+ name = '%s (%.1fms)' % [name || 'SQL', ms]
@logger.debug(format_log_entry(name, sql.squeeze(' ')))
end
end
@@ -195,9 +195,9 @@ module ActiveRecord
def log(sql, name)
if block_given?
result = nil
- seconds = Benchmark.realtime { result = yield }
- @runtime += seconds
- log_info(sql, name, seconds)
+ ms = Benchmark.ms { result = yield }
+ @runtime += ms
+ log_info(sql, name, ms)
result
else
log_info(sql, name, 0)
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 3fbbb46f73..b2345fd571 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -13,23 +13,25 @@ module MysqlCompat #:nodoc:
# C driver >= 2.7 returns null values in each_hash
if Mysql.const_defined?(:VERSION) && (Mysql::VERSION.is_a?(String) || Mysql::VERSION >= 20700)
target.class_eval <<-'end_eval'
- def all_hashes
- rows = []
- each_hash { |row| rows << row }
- rows
- end
+ def all_hashes # def all_hashes
+ rows = [] # rows = []
+ each_hash { |row| rows << row } # each_hash { |row| rows << row }
+ rows # rows
+ end # end
end_eval
# adapters before 2.7 don't have a version constant
# and don't return null values in each_hash
else
target.class_eval <<-'end_eval'
- def all_hashes
- rows = []
- all_fields = fetch_fields.inject({}) { |fields, f| fields[f.name] = nil; fields }
- each_hash { |row| rows << all_fields.dup.update(row) }
- rows
- end
+ def all_hashes # def all_hashes
+ rows = [] # rows = []
+ all_fields = fetch_fields.inject({}) { |fields, f| # all_fields = fetch_fields.inject({}) { |fields, f|
+ fields[f.name] = nil; fields # fields[f.name] = nil; fields
+ } # }
+ each_hash { |row| rows << all_fields.dup.update(row) } # each_hash { |row| rows << all_fields.dup.update(row) }
+ rows # rows
+ end # end
end_eval
end
@@ -312,6 +314,7 @@ module ActiveRecord
rows
end
+ # Executes a SQL query and returns a MySQL::Result object. Note that you have to free the Result object after you're done using it.
def execute(sql, name = nil) #:nodoc:
log(sql, name) { @connection.query(sql) }
rescue ActiveRecord::StatementInvalid => exception
@@ -429,7 +432,9 @@ module ActiveRecord
def tables(name = nil) #:nodoc:
tables = []
- execute("SHOW TABLES", name).each { |field| tables << field[0] }
+ result = execute("SHOW TABLES", name)
+ result.each { |field| tables << field[0] }
+ result.free
tables
end
@@ -440,7 +445,8 @@ module ActiveRecord
def indexes(table_name, name = nil)#:nodoc:
indexes = []
current_index = nil
- execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name).each do |row|
+ result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name)
+ result.each do |row|
if current_index != row[2]
next if row[2] == "PRIMARY" # skip the primary key
current_index = row[2]
@@ -449,13 +455,16 @@ module ActiveRecord
indexes.last.columns << row[4]
end
+ result.free
indexes
end
def columns(table_name, name = nil)#:nodoc:
sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
columns = []
- execute(sql, name).each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
+ result = execute(sql, name)
+ result.each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
+ result.free
columns
end
@@ -536,9 +545,11 @@ module ActiveRecord
# Returns a table's primary key and belonging sequence.
def pk_and_sequence_for(table) #:nodoc:
keys = []
- execute("describe #{quote_table_name(table)}").each_hash do |h|
+ result = execute("describe #{quote_table_name(table)}")
+ result.each_hash do |h|
keys << h["Field"]if h["Key"] == "PRI"
end
+ result.free
keys.length == 1 ? [keys.first, nil] : nil
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index c4ef2be82e..5a8d99924d 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -937,13 +937,13 @@ module ActiveRecord
# should know about this but can't detect it there, so deal with it here.
money_precision = (postgresql_version >= 80300) ? 19 : 10
PostgreSQLColumn.module_eval(<<-end_eval)
- def extract_precision(sql_type)
- if sql_type =~ /^money$/
- #{money_precision}
- else
- super
- end
- end
+ def extract_precision(sql_type) # def extract_precision(sql_type)
+ if sql_type =~ /^money$/ # if sql_type =~ /^money$/
+ #{money_precision} # 19
+ else # else
+ super # super
+ end # end
+ end # end
end_eval
configure_connection
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 84f8c0284e..9387cf8827 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -402,6 +402,10 @@ module ActiveRecord
end
def add_column(table_name, column_name, type, options = {}) #:nodoc:
+ if @connection.respond_to?(:transaction_active?) && @connection.transaction_active?
+ raise StatementInvalid, 'Cannot add columns to a SQLite database while inside a transaction'
+ end
+
alter_table(table_name) do |definition|
definition.column(column_name, type, options)
end
diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb
index a1760875ba..4c899f58e5 100644
--- a/activerecord/lib/active_record/dirty.rb
+++ b/activerecord/lib/active_record/dirty.rb
@@ -174,7 +174,7 @@ module ActiveRecord
alias_attribute_without_dirty(new_name, old_name)
DIRTY_SUFFIXES.each do |suffix|
module_eval <<-STR, __FILE__, __LINE__+1
- def #{new_name}#{suffix}; self.#{old_name}#{suffix}; end
+ def #{new_name}#{suffix}; self.#{old_name}#{suffix}; end # def subject_changed?; self.title_changed?; end
STR
end
end
diff --git a/activerecord/lib/active_record/dynamic_scope_match.rb b/activerecord/lib/active_record/dynamic_scope_match.rb
new file mode 100644
index 0000000000..f796ba669a
--- /dev/null
+++ b/activerecord/lib/active_record/dynamic_scope_match.rb
@@ -0,0 +1,25 @@
+module ActiveRecord
+ class DynamicScopeMatch
+ def self.match(method)
+ ds_match = self.new(method)
+ ds_match.scope ? ds_match : nil
+ end
+
+ def initialize(method)
+ @scope = true
+ case method.to_s
+ when /^scoped_by_([_a-zA-Z]\w*)$/
+ names = $1
+ else
+ @scope = nil
+ end
+ @attribute_names = names && names.split('_and_')
+ end
+
+ attr_reader :scope, :attribute_names
+
+ def scope?
+ !@scope.nil?
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb
index a8af89fcb9..eb92bc2545 100644
--- a/activerecord/lib/active_record/query_cache.rb
+++ b/activerecord/lib/active_record/query_cache.rb
@@ -1,20 +1,32 @@
module ActiveRecord
- module QueryCache
- # Enable the query cache within the block if Active Record is configured.
- def cache(&block)
- if ActiveRecord::Base.configurations.blank?
- yield
- else
- connection.cache(&block)
+ class QueryCache
+ module ClassMethods
+ # Enable the query cache within the block if Active Record is configured.
+ def cache(&block)
+ if ActiveRecord::Base.configurations.blank?
+ yield
+ else
+ connection.cache(&block)
+ end
end
+
+ # Disable the query cache within the block if Active Record is configured.
+ def uncached(&block)
+ if ActiveRecord::Base.configurations.blank?
+ yield
+ else
+ connection.uncached(&block)
+ end
+ end
+ end
+
+ def initialize(app)
+ @app = app
end
- # Disable the query cache within the block if Active Record is configured.
- def uncached(&block)
- if ActiveRecord::Base.configurations.blank?
- yield
- else
- connection.uncached(&block)
+ def call(env)
+ ActiveRecord::Base.cache do
+ @app.call(env)
end
end
end
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index d171b742f5..4749823b94 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -23,11 +23,12 @@ module ActiveRecord #:nodoc:
# </topic>
#
# This behavior can be controlled with <tt>:only</tt>, <tt>:except</tt>,
- # <tt>:skip_instruct</tt>, <tt>:skip_types</tt> and <tt>:dasherize</tt>.
+ # <tt>:skip_instruct</tt>, <tt>:skip_types</tt>, <tt>:dasherize</tt> and <tt>:camelize</tt> .
# The <tt>:only</tt> and <tt>:except</tt> options are the same as for the
# +attributes+ method. The default is to dasherize all column names, but you
- # can disable this setting <tt>:dasherize</tt> to +false+. To not have the
- # column type included in the XML output set <tt>:skip_types</tt> to +true+.
+ # can disable this setting <tt>:dasherize</tt> to +false+. Setting <tt>:camelize</tt>
+ # to +true+ will camelize all column names - this also overrides <tt>:dasherize</tt>.
+ # To not have the column type included in the XML output set <tt>:skip_types</tt> to +true+.
#
# For instance:
#
@@ -178,13 +179,22 @@ module ActiveRecord #:nodoc:
def root
root = (options[:root] || @record.class.to_s.underscore).to_s
- dasherize? ? root.dasherize : root
+ reformat_name(root)
end
def dasherize?
!options.has_key?(:dasherize) || options[:dasherize]
end
+ def camelize?
+ options.has_key?(:camelize) && options[:camelize]
+ end
+
+ def reformat_name(name)
+ name = name.camelize if camelize?
+ dasherize? ? name.dasherize : name
+ end
+
def serializable_attributes
serializable_attribute_names.collect { |name| Attribute.new(name, @record) }
end
@@ -212,7 +222,7 @@ module ActiveRecord #:nodoc:
def add_tag(attribute)
builder.tag!(
- dasherize? ? attribute.name.dasherize : attribute.name,
+ reformat_name(attribute.name),
attribute.value.to_s,
attribute.decorations(!options[:skip_types])
)
@@ -220,8 +230,7 @@ module ActiveRecord #:nodoc:
def add_associations(association, records, opts)
if records.is_a?(Enumerable)
- tag = association.to_s
- tag = tag.dasherize if dasherize?
+ tag = reformat_name(association.to_s)
if records.empty?
builder.tag!(tag, :type => :array)
else
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
new file mode 100644
index 0000000000..bd198c03b2
--- /dev/null
+++ b/activerecord/lib/active_record/session_store.rb
@@ -0,0 +1,319 @@
+module ActiveRecord
+ # 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+ (text, or longtext if your session data exceeds 65K), 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/environment.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)
+ # 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 < ActionController::Session::AbstractStore
+ # The default Active Record class.
+ class Session < ActiveRecord::Base
+ ##
+ # :singleton-method:
+ # Customizable data column name. Defaults to 'data'.
+ cattr_accessor :data_column_name
+ self.data_column_name = 'data'
+
+ before_save :marshal_data!
+ before_save :raise_on_session_data_overflow!
+
+ class << self
+ # Don't try to reload ARStore::Session in dev mode.
+ def reloadable? #:nodoc:
+ false
+ end
+
+ 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
+
+ def marshal(data)
+ ActiveSupport::Base64.encode64(Marshal.dump(data)) if data
+ end
+
+ def unmarshal(data)
+ Marshal.load(ActiveSupport::Base64.decode64(data)) if data
+ end
+
+ def create_table!
+ connection.execute <<-end_sql
+ CREATE TABLE #{table_name} (
+ id INTEGER PRIMARY KEY,
+ #{connection.quote_column_name('session_id')} TEXT UNIQUE,
+ #{connection.quote_column_name(@@data_column_name)} TEXT(255)
+ )
+ end_sql
+ end
+
+ def drop_table!
+ connection.execute "DROP TABLE #{table_name}"
+ end
+
+ private
+ # 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
+ def self.find_by_session_id(session_id)
+ find :first, :conditions => ["session_id #{attribute_condition(session_id)}", session_id]
+ end
+ end
+ end
+ 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 if !loaded?
+ write_attribute(@@data_column_name, self.class.marshal(self.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 if !loaded?
+ limit = self.class.data_column_size_limit
+ if loaded? and 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
+ #
+ # ActiveSupport::Base64.encode64(Marshal.dump(data))
+ #
+ # and unmarshaling data is
+ #
+ # Marshal.load(ActiveSupport::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
+ ##
+ # :singleton-method:
+ # Use the ActiveRecord::Base.connection by default.
+ cattr_accessor :connection
+
+ ##
+ # :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
+ def connection
+ @@connection ||= ActiveRecord::Base.connection
+ 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 * FROM #{@@table_name} WHERE #{@@session_id_column}=#{@@connection.quote(session_id)}")
+ new(:session_id => session_id, :marshaled_data => record['data'])
+ end
+ end
+
+ def marshal(data)
+ ActiveSupport::Base64.encode64(Marshal.dump(data)) if data
+ end
+
+ def unmarshal(data)
+ Marshal.load(ActiveSupport::Base64.decode64(data)) if data
+ end
+
+ def create_table!
+ @@connection.execute <<-end_sql
+ CREATE TABLE #{table_name} (
+ id INTEGER PRIMARY KEY,
+ #{@@connection.quote_column_name(session_id_column)} TEXT UNIQUE,
+ #{@@connection.quote_column_name(data_column)} TEXT
+ )
+ end_sql
+ end
+
+ def drop_table!
+ @@connection.execute "DROP TABLE #{table_name}"
+ end
+ end
+
+ attr_reader :session_id
+ 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, @data, @marshaled_data = attributes[:session_id], attributes[:data], attributes[:marshaled_data]
+ @new_record = @marshaled_data.nil?
+ end
+
+ def new_record?
+ @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 if !loaded?
+ marshaled_data = self.class.marshal(data)
+
+ if @new_record
+ @new_record = false
+ @@connection.update <<-end_sql, 'Create session'
+ INSERT INTO #{@@table_name} (
+ #{@@connection.quote_column_name(@@session_id_column)},
+ #{@@connection.quote_column_name(@@data_column)} )
+ VALUES (
+ #{@@connection.quote(session_id)},
+ #{@@connection.quote(marshaled_data)} )
+ end_sql
+ else
+ @@connection.update <<-end_sql, 'Update session'
+ UPDATE #{@@table_name}
+ SET #{@@connection.quote_column_name(@@data_column)}=#{@@connection.quote(marshaled_data)}
+ WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)}
+ end_sql
+ end
+ end
+
+ def destroy
+ unless @new_record
+ @@connection.delete <<-end_sql, 'Destroy session'
+ DELETE FROM #{@@table_name}
+ WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)}
+ end_sql
+ end
+ 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'.freeze
+
+ private
+ def get_session(env, sid)
+ Base.silence do
+ sid ||= generate_sid
+ session = @@session_class.find_by_session_id(sid)
+ session ||= @@session_class.new(:session_id => sid, :data => {})
+ env[SESSION_RECORD_KEY] = session
+ [sid, session.data]
+ end
+ end
+
+ def set_session(env, sid, session_data)
+ Base.silence do
+ record = env[SESSION_RECORD_KEY]
+ 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
+
+ return true
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index a9e0efa6fe..8dbe80a01a 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -23,8 +23,8 @@ module ActiveRecord
write_attribute('created_at', t) if respond_to?(:created_at) && created_at.nil?
write_attribute('created_on', t) if respond_to?(:created_on) && created_on.nil?
- write_attribute('updated_at', t) if respond_to?(:updated_at)
- write_attribute('updated_on', t) if respond_to?(:updated_on)
+ write_attribute('updated_at', t) if respond_to?(:updated_at) && updated_at.nil?
+ write_attribute('updated_on', t) if respond_to?(:updated_on) && updated_on.nil?
end
create_without_timestamps
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 1e0185d39c..aaa298dc49 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -200,7 +200,7 @@ module ActiveRecord
end
def save_with_transactions! #:nodoc:
- rollback_active_record_state! { transaction { save_without_transactions! } }
+ rollback_active_record_state! { self.class.transaction { save_without_transactions! } }
end
# Reset id and @new_record if the transaction rolls back.
@@ -228,7 +228,7 @@ module ActiveRecord
# instance.
def with_transaction_returning_status(method, *args)
status = nil
- transaction do
+ self.class.transaction do
status = send(method, *args)
raise ActiveRecord::Rollback unless status
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 4b275ddd70..6a9690ba85 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -203,7 +203,6 @@ module ActiveRecord
if attr == "base"
full_messages << message
else
- #key = :"activerecord.att.#{@base.class.name.underscore.to_sym}.#{attr}"
attr_name = @base.class.human_attribute_name(attr)
full_messages << attr_name + I18n.t('activerecord.errors.format.separator', :default => ' ') + message
end
@@ -1049,15 +1048,15 @@ module ActiveRecord
protected
# Overwrite this method for validation checks on all saves and use <tt>Errors.add(field, msg)</tt> for invalid attributes.
- def validate #:doc:
+ def validate
end
# Overwrite this method for validation checks used only on creation.
- def validate_on_create #:doc:
+ def validate_on_create
end
# Overwrite this method for validation checks used only on updates.
- def validate_on_update # :doc:
+ def validate_on_update
end
end
end
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index 8c9ae8a031..45e74ea024 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -104,6 +104,14 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
authors.first.posts.first.special_comments.first.post.very_special_comment
end
end
+
+ def test_eager_association_loading_where_first_level_returns_nil
+ authors = Author.find(:all, :include => {:post_about_thinking => :comments}, :order => 'authors.id DESC')
+ assert_equal [authors(:mary), authors(:david)], authors
+ assert_no_queries do
+ authors[1].post_about_thinking.comments.first
+ end
+ end
end
require 'models/vertex'
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 3c8408d14b..14099d4176 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1,6 +1,7 @@
require "cases/helper"
require 'models/post'
require 'models/tagging'
+require 'models/tag'
require 'models/comment'
require 'models/author'
require 'models/category'
@@ -145,7 +146,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_finding_with_includes_on_null_belongs_to_association_with_same_include_includes_only_once
post = posts(:welcome)
post.update_attributes!(:author => nil)
- post = assert_queries(2) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author which is null so no query for the address
+ post = assert_queries(1) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author which is null so no query for the author or address
assert_no_queries do
assert_equal nil, post.author_with_address
end
@@ -705,4 +706,117 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_order_on_join_table_with_include_and_limit
assert_equal 5, Developer.find(:all, :include => 'projects', :order => 'developers_projects.joined_on DESC', :limit => 5).size
end
+
+ def test_eager_loading_with_order_on_joined_table_preloads
+ posts = assert_queries(2) do
+ Post.find(:all, :joins => :comments, :include => :author, :order => 'comments.id DESC')
+ end
+ assert_equal posts(:eager_other), posts[0]
+ assert_equal authors(:mary), assert_no_queries { posts[0].author}
+ end
+
+ def test_eager_loading_with_conditions_on_joined_table_preloads
+ posts = assert_queries(2) do
+ Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => [:comments], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
+ end
+ assert_equal [posts(:welcome)], posts
+ assert_equal authors(:david), assert_no_queries { posts[0].author}
+
+ posts = assert_queries(2) do
+ Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => [:comments], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
+ end
+ assert_equal [posts(:welcome)], posts
+ assert_equal authors(:david), assert_no_queries { posts[0].author}
+
+ posts = assert_queries(2) do
+ Post.find(:all, :include => :author, :joins => {:taggings => :tag}, :conditions => "tags.name = 'General'", :order => 'posts.id')
+ end
+ assert_equal posts(:welcome, :thinking), posts
+
+ posts = assert_queries(2) do
+ Post.find(:all, :include => :author, :joins => {:taggings => {:tag => :taggings}}, :conditions => "taggings_tags.super_tag_id=2", :order => 'posts.id')
+ end
+ assert_equal posts(:welcome, :thinking), posts
+
+ end
+
+ def test_eager_loading_with_conditions_on_string_joined_table_preloads
+ posts = assert_queries(2) do
+ Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => "INNER JOIN comments on comments.post_id = posts.id", :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
+ end
+ assert_equal [posts(:welcome)], posts
+ assert_equal authors(:david), assert_no_queries { posts[0].author}
+
+ posts = assert_queries(2) do
+ Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => ["INNER JOIN comments on comments.post_id = posts.id"], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
+ end
+ assert_equal [posts(:welcome)], posts
+ assert_equal authors(:david), assert_no_queries { posts[0].author}
+
+ end
+
+ def test_eager_loading_with_select_on_joined_table_preloads
+ posts = assert_queries(2) do
+ Post.find(:all, :select => 'posts.*, authors.name as author_name', :include => :comments, :joins => :author, :order => 'posts.id')
+ end
+ assert_equal 'David', posts[0].author_name
+ assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments}
+ end
+
+ def test_eager_loading_with_conditions_on_join_model_preloads
+ authors = assert_queries(2) do
+ Author.find(:all, :include => :author_address, :joins => :comments, :conditions => "posts.title like 'Welcome%'")
+ end
+ assert_equal authors(:david), authors[0]
+ assert_equal author_addresses(:david_address), authors[0].author_address
+ end
+
+ def test_preload_belongs_to_uses_exclusive_scope
+ people = Person.males.find(:all, :include => :primary_contact)
+ assert_not_equal people.length, 0
+ people.each do |person|
+ assert_no_queries {assert_not_nil person.primary_contact}
+ assert_equal Person.find(person.id).primary_contact, person.primary_contact
+ end
+ end
+
+ def test_preload_has_many_uses_exclusive_scope
+ people = Person.males.find :all, :include => :agents
+ people.each do |person|
+ assert_equal Person.find(person.id).agents, person.agents
+ end
+ end
+
+ def test_preload_has_many_using_primary_key
+ expected = Firm.find(:first).clients_using_primary_key.to_a
+ firm = Firm.find :first, :include => :clients_using_primary_key
+ assert_no_queries do
+ assert_equal expected, firm.clients_using_primary_key
+ end
+ end
+
+ def test_include_has_many_using_primary_key
+ expected = Firm.find(1).clients_using_primary_key.sort_by &:name
+ firm = Firm.find 1, :include => :clients_using_primary_key, :order => 'clients_using_primary_keys_companies.name'
+ assert_no_queries do
+ assert_equal expected, firm.clients_using_primary_key
+ end
+ end
+
+ def test_preload_has_one_using_primary_key
+ expected = Firm.find(:first).account_using_primary_key
+ firm = Firm.find :first, :include => :account_using_primary_key
+ assert_no_queries do
+ assert_equal expected, firm.account_using_primary_key
+ end
+ end
+
+ def test_include_has_one_using_primary_key
+ expected = Firm.find(1).account_using_primary_key
+ firm = Firm.find(:all, :include => :account_using_primary_key, :order => 'accounts.id').detect {|f| f.id == 1}
+ assert_no_queries do
+ assert_equal expected, firm.account_using_primary_key
+ end
+ 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 816ceb6855..a5ae5cd8ec 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -665,6 +665,19 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, Client.find_all_by_client_of(firm.id).size
end
+ def test_dependent_association_respects_optional_hash_conditions_on_delete
+ firm = companies(:odegy)
+ Client.create(:client_of => firm.id, :name => "BigShot Inc.")
+ Client.create(:client_of => firm.id, :name => "SmallTime Inc.")
+ # only one of two clients is included in the association due to the :conditions key
+ assert_equal 2, Client.find_all_by_client_of(firm.id).size
+ assert_equal 1, firm.dependent_sanitized_conditional_clients_of_firm.size
+ firm.destroy
+ # only the correctly associated client should have been deleted
+ assert_equal 1, Client.find_all_by_client_of(firm.id).size
+ end
+
+
def test_creation_respects_hash_condition
ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.build
@@ -1102,5 +1115,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert !client_association.respond_to?(:private_method)
assert client_association.respond_to?(:private_method, true)
end
+
+ def test_creating_using_primary_key
+ firm = Firm.find(:first)
+ client = firm.clients_using_primary_key.create!(:name => 'test')
+ assert_equal firm.name, client.firm_name
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index a07f4bcbdd..ad6a5d6840 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -3,6 +3,9 @@ require 'models/post'
require 'models/person'
require 'models/reader'
require 'models/comment'
+require 'models/tag'
+require 'models/tagging'
+require 'models/author'
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
fixtures :posts, :readers, :people, :comments, :authors
@@ -201,6 +204,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal 2, people(:michael).posts.count(:include => :readers)
end
+ def test_inner_join_with_quoted_table_name
+ assert_equal 2, people(:michael).jobs.size
+ end
+
def test_get_ids
assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort
end
diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb
index 7d418de965..f65d76e2ce 100644
--- a/activerecord/test/cases/associations/has_one_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -1,5 +1,6 @@
require "cases/helper"
require 'models/club'
+require 'models/member_type'
require 'models/member'
require 'models/membership'
require 'models/sponsor'
@@ -7,7 +8,7 @@ require 'models/organization'
require 'models/member_detail'
class HasOneThroughAssociationsTest < ActiveRecord::TestCase
- fixtures :members, :clubs, :memberships, :sponsors, :organizations
+ fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations
def setup
@member = members(:groucho)
@@ -158,4 +159,18 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
assert @new_organization.members.include?(@member)
end
+ def test_preloading_has_one_through_on_belongs_to
+ assert_not_nil @member.member_type
+ @organization = organizations(:nsa)
+ @member_detail = MemberDetail.new
+ @member.member_detail = @member_detail
+ @member.organization = @organization
+ @member_details = assert_queries(3) do
+ MemberDetail.find(:all, :include => :member_type)
+ end
+ @new_detail = @member_details[0]
+ assert @new_detail.loaded_member_type?
+ assert_not_nil assert_no_queries { @new_detail.member_type }
+ end
+
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 5f54931d00..0f03dae829 100755
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -16,6 +16,7 @@ require 'models/post'
require 'models/comment'
require 'models/minimalistic'
require 'models/warehouse_thing'
+require 'models/parrot'
require 'rexml/document'
class Category < ActiveRecord::Base; end
@@ -1197,6 +1198,11 @@ class BasicsTest < ActiveRecord::TestCase
assert b_true.value?
end
+ def test_new_record_returns_boolean
+ assert_equal Topic.new.new_record?, true
+ assert_equal Topic.find(1).new_record?, false
+ end
+
def test_clone
topic = Topic.find(1)
cloned_topic = nil
@@ -2071,6 +2077,15 @@ class BasicsTest < ActiveRecord::TestCase
ActiveRecord::Base.logger = original_logger
end
+ def test_create_with_custom_timestamps
+ custom_datetime = 1.hour.ago.beginning_of_day
+
+ %w(created_at created_on updated_at updated_on).each do |attribute|
+ parrot = LiveParrot.create(:name => "colombian", attribute => custom_datetime)
+ assert_equal custom_datetime, parrot[attribute]
+ end
+ end
+
private
def with_kcode(kcode)
if RUBY_VERSION < '1.9'
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 8bd0dd0f6e..080f6a7007 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -171,8 +171,9 @@ class CalculationsTest < ActiveRecord::TestCase
Account.expects(:columns).at_least_once.returns([column])
c = Account.count(:all, :group => :firm)
- assert_equal Firm, c.first.first.class
- assert_equal 1, c.first.last
+ first_key = c.keys.first
+ assert_equal Firm, first_key.class
+ assert_equal 1, c[first_key]
end
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index c18ecf6f8f..ccd04b67f6 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -9,6 +9,8 @@ require 'active_record/test_case'
require 'active_record/fixtures'
require 'connection'
+require 'cases/repair_helper'
+
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
@@ -24,6 +26,7 @@ end
def uses_mocha(description)
require 'rubygems'
+ gem 'mocha', '>= 0.9.3'
require 'mocha'
yield
rescue LoadError
@@ -59,6 +62,8 @@ end
class ActiveSupport::TestCase
include ActiveRecord::TestFixtures
+ include ActiveRecord::Testing::RepairHelper
+
self.fixture_path = FIXTURES_ROOT
self.use_instantiated_fixtures = false
self.use_transactional_fixtures = true
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index 6372b4f6aa..80a06116ad 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -27,6 +27,24 @@ class MethodScopingTest < ActiveRecord::TestCase
end
end
+ def test_scoped_find_last
+ highest_salary = Developer.find(:first, :order => "salary DESC")
+
+ Developer.with_scope(:find => { :order => "salary" }) do
+ assert_equal highest_salary, Developer.last
+ end
+ end
+
+ def test_scoped_find_last_preserves_scope
+ lowest_salary = Developer.find(:first, :order => "salary ASC")
+ highest_salary = Developer.find(:first, :order => "salary DESC")
+
+ Developer.with_scope(:find => { :order => "salary" }) do
+ assert_equal highest_salary, Developer.last
+ assert_equal lowest_salary, Developer.first
+ end
+ end
+
def test_scoped_find_combines_conditions
Developer.with_scope(:find => { :conditions => "salary = 9000" }) do
assert_equal developers(:poor_jamis), Developer.find(:first, :conditions => "name = 'Jamis'")
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 64e899780c..bab842cf66 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -254,7 +254,7 @@ class NamedScopeTest < ActiveRecord::TestCase
end
def test_should_use_where_in_query_for_named_scope
- assert_equal Developer.find_all_by_name('Jamis'), Developer.find_all_by_id(Developer.jamises)
+ assert_equal Developer.find_all_by_name('Jamis').to_set, Developer.find_all_by_id(Developer.jamises).to_set
end
def test_size_should_use_count_when_results_are_not_loaded
@@ -278,3 +278,23 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_equal post.comments.size, Post.scoped(:joins => join).scoped(:joins => join, :conditions => "posts.id = #{post.id}").size
end
end
+
+class DynamicScopeMatchTest < ActiveRecord::TestCase
+ def test_scoped_by_no_match
+ assert_nil ActiveRecord::DynamicScopeMatch.match("not_scoped_at_all")
+ end
+
+ def test_scoped_by
+ match = ActiveRecord::DynamicScopeMatch.match("scoped_by_age_and_sex_and_location")
+ assert_not_nil match
+ assert match.scope?
+ assert_equal %w(age sex location), match.attribute_names
+ end
+end
+
+class DynamicScopeTest < ActiveRecord::TestCase
+ def test_dynamic_scope
+ assert_equal Post.scoped_by_author_id(1).find(1), Post.find(1)
+ assert_equal Post.scoped_by_author_id_and_title(1, "Welcome to the weblog").first, Post.find(:first, :conditions => { :author_id => 1, :title => "Welcome to the weblog"})
+ end
+end
diff --git a/activerecord/test/cases/reload_models_test.rb b/activerecord/test/cases/reload_models_test.rb
index 411b5f6afa..0d16a3526f 100644
--- a/activerecord/test/cases/reload_models_test.rb
+++ b/activerecord/test/cases/reload_models_test.rb
@@ -3,6 +3,8 @@ require 'models/owner'
require 'models/pet'
class ReloadModelsTest < ActiveRecord::TestCase
+ fixtures :pets
+
def test_has_one_with_reload
pet = Pet.find_by_name('parrot')
pet.owner = Owner.find_by_name('ashley')
@@ -17,4 +19,4 @@ class ReloadModelsTest < ActiveRecord::TestCase
pet.owner = Owner.find_by_name('ashley')
assert_equal pet.owner, Owner.find_by_name('ashley')
end
-end \ No newline at end of file
+end
diff --git a/activerecord/test/cases/repair_helper.rb b/activerecord/test/cases/repair_helper.rb
new file mode 100644
index 0000000000..0155668811
--- /dev/null
+++ b/activerecord/test/cases/repair_helper.rb
@@ -0,0 +1,50 @@
+module ActiveRecord
+ module Testing
+ module RepairHelper
+ def self.included(base)
+ base.class_eval do
+ extend ClassMethods
+ end
+ end
+
+ module Toolbox
+ def self.record_validations(*model_classes)
+ model_classes.inject({}) do |repair, klass|
+ repair[klass] ||= {}
+ [:validate, :validate_on_create, :validate_on_update].each do |callback|
+ the_callback = klass.instance_variable_get("@#{callback.to_s}_callbacks")
+ repair[klass][callback] = (the_callback.nil? ? nil : the_callback.dup)
+ end
+ repair
+ end
+ end
+
+ def self.reset_validations(recorded)
+ recorded.each do |klass, repairs|
+ [:validate, :validate_on_create, :validate_on_update].each do |callback|
+ klass.instance_variable_set("@#{callback.to_s}_callbacks", repairs[callback])
+ end
+ end
+ end
+ end
+
+ module ClassMethods
+ def repair_validations(*model_classes)
+ setup do
+ @validation_repairs = ActiveRecord::Testing::RepairHelper::Toolbox.record_validations(*model_classes)
+ end
+ teardown do
+ ActiveRecord::Testing::RepairHelper::Toolbox.reset_validations(@validation_repairs)
+ end
+ end
+ end
+
+ def repair_validations(*model_classes, &block)
+ validation_repairs = ActiveRecord::Testing::RepairHelper::Toolbox.record_validations(*model_classes)
+ return block.call
+ ensure
+ ActiveRecord::Testing::RepairHelper::Toolbox.reset_validations(validation_repairs)
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb
index f59e3f7001..e893a704f1 100644
--- a/activerecord/test/cases/validations_i18n_test.rb
+++ b/activerecord/test/cases/validations_i18n_test.rb
@@ -506,7 +506,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase
# validates_length_of :is w/o mocha
- def test_validates_length_of_within_finds_custom_model_key_translation
+ def test_validates_length_of_is_finds_custom_model_key_translation
I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
@@ -515,7 +515,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase
assert_equal 'custom message', @topic.errors.on(:title)
end
- def test_validates_length_of_within_finds_global_default_translation
+ def test_validates_length_of_is_finds_global_default_translation
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
Topic.validates_length_of :title, :is => 5
@@ -525,7 +525,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase
# validates_uniqueness_of w/o mocha
- def test_validates_length_of_within_finds_custom_model_key_translation
+ def test_validates_length_of_is_finds_custom_model_key_translation
I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}}
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
@@ -534,7 +534,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase
assert_equal 'custom message', @topic.errors.on(:title)
end
- def test_validates_length_of_within_finds_global_default_translation
+ def test_validates_length_of_is_finds_global_default_translation
I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}}
Topic.validates_length_of :title, :is => 5
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index c049659327..380d8ac260 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -6,6 +6,8 @@ require 'models/person'
require 'models/developer'
require 'models/warehouse_thing'
require 'models/guid'
+require 'models/owner'
+require 'models/pet'
# The following methods in Topic are used in test_conditional_validation_*
class Topic
@@ -31,10 +33,6 @@ class UniqueReply < Reply
validates_uniqueness_of :content, :scope => 'parent_id'
end
-class PlagiarizedReply < Reply
- validates_acceptance_of :author_name
-end
-
class SillyUniqueReply < UniqueReply
end
@@ -58,11 +56,9 @@ end
class ValidationsTest < ActiveRecord::TestCase
fixtures :topics, :developers, 'warehouse-things'
- def setup
- Topic.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- Topic.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- Topic.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- end
+ # Most of the tests mess with the validations of Topic, so lets repair it all the time.
+ # Other classes we mess with will be dealt with in the specific tests
+ repair_validations(Topic)
def test_single_field_validation
r = Reply.new
@@ -134,7 +130,7 @@ class ValidationsTest < ActiveRecord::TestCase
Reply.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }])
end
end
-
+
def test_exception_on_create_bang_with_block
assert_raises(ActiveRecord::RecordInvalid) do
Reply.create!({ "title" => "OK" }) do |r|
@@ -142,7 +138,7 @@ class ValidationsTest < ActiveRecord::TestCase
end
end
end
-
+
def test_exception_on_create_bang_many_with_block
assert_raises(ActiveRecord::RecordInvalid) do
Reply.create!([{ "title" => "OK" }, { "title" => "Wrong Create" }]) do |r|
@@ -229,21 +225,16 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_validates_each
- perform = true
hits = 0
Topic.validates_each(:title, :content, [:title, :content]) do |record, attr|
- if perform
- record.errors.add attr, 'gotcha'
- hits += 1
- end
+ record.errors.add attr, 'gotcha'
+ hits += 1
end
t = Topic.new("title" => "valid", "content" => "whatever")
assert !t.save
assert_equal 4, hits
assert_equal %w(gotcha gotcha), t.errors.on(:title)
assert_equal %w(gotcha gotcha), t.errors.on(:content)
- ensure
- perform = false
end
def test_no_title_confirmation
@@ -315,8 +306,12 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_validates_acceptance_of_as_database_column
- reply = PlagiarizedReply.create("author_name" => "Dan Brown")
- assert_equal "Dan Brown", reply["author_name"]
+ repair_validations(Reply) do
+ Reply.validates_acceptance_of(:author_name)
+
+ reply = Reply.create("author_name" => "Dan Brown")
+ assert_equal "Dan Brown", reply["author_name"]
+ end
end
def test_validates_acceptance_of_with_non_existant_table
@@ -372,22 +367,24 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_validate_uniqueness_with_scope
- Reply.validates_uniqueness_of(:content, :scope => "parent_id")
+ repair_validations(Reply) do
+ Reply.validates_uniqueness_of(:content, :scope => "parent_id")
- t = Topic.create("title" => "I'm unique!")
+ t = Topic.create("title" => "I'm unique!")
- r1 = t.replies.create "title" => "r1", "content" => "hello world"
- assert r1.valid?, "Saving r1"
+ r1 = t.replies.create "title" => "r1", "content" => "hello world"
+ assert r1.valid?, "Saving r1"
- r2 = t.replies.create "title" => "r2", "content" => "hello world"
- assert !r2.valid?, "Saving r2 first time"
+ r2 = t.replies.create "title" => "r2", "content" => "hello world"
+ assert !r2.valid?, "Saving r2 first time"
- r2.content = "something else"
- assert r2.save, "Saving r2 second time"
+ r2.content = "something else"
+ assert r2.save, "Saving r2 second time"
- t2 = Topic.create("title" => "I'm unique too!")
- r3 = t2.replies.create "title" => "r3", "content" => "hello world"
- assert r3.valid?, "Saving r3"
+ t2 = Topic.create("title" => "I'm unique too!")
+ r3 = t2.replies.create "title" => "r3", "content" => "hello world"
+ assert r3.valid?, "Saving r3"
+ end
end
def test_validate_uniqueness_scoped_to_defining_class
@@ -406,27 +403,29 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_validate_uniqueness_with_scope_array
- Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
+ repair_validations(Reply) do
+ Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
- t = Topic.create("title" => "The earth is actually flat!")
+ t = Topic.create("title" => "The earth is actually flat!")
- r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply"
- assert r1.valid?, "Saving r1"
+ r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply"
+ assert r1.valid?, "Saving r1"
- r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..."
- assert !r2.valid?, "Saving r2. Double reply by same author."
+ r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..."
+ assert !r2.valid?, "Saving r2. Double reply by same author."
- r2.author_email_address = "jeremy_alt_email@rubyonrails.com"
- assert r2.save, "Saving r2 the second time."
+ r2.author_email_address = "jeremy_alt_email@rubyonrails.com"
+ assert r2.save, "Saving r2 the second time."
- r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic"
- assert !r3.valid?, "Saving r3"
+ r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic"
+ assert !r3.valid?, "Saving r3"
- r3.author_name = "jj"
- assert r3.save, "Saving r3 the second time."
+ r3.author_name = "jj"
+ assert r3.save, "Saving r3 the second time."
- r3.author_name = "jeremy"
- assert !r3.save, "Saving r3 the third time."
+ r3.author_name = "jeremy"
+ assert !r3.save, "Saving r3 the third time."
+ end
end
def test_validate_case_insensitive_uniqueness
@@ -523,10 +522,12 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_validate_uniqueness_with_columns_which_are_sql_keywords
- Guid.validates_uniqueness_of :key
- g = Guid.new
- g.key = "foo"
- assert_nothing_raised { !g.valid? }
+ repair_validations(Guid) do
+ Guid.validates_uniqueness_of :key
+ g = Guid.new
+ g.key = "foo"
+ assert_nothing_raised { !g.valid? }
+ end
end
def test_validate_straight_inheritance_uniqueness
@@ -648,10 +649,12 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_numericality_with_getter_method
- Developer.validates_numericality_of( :salary )
- developer = Developer.new("name" => "michael", "salary" => nil)
- developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
- assert developer.valid?
+ repair_validations(Developer) do
+ Developer.validates_numericality_of( :salary )
+ developer = Developer.new("name" => "michael", "salary" => nil)
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
+ assert developer.valid?
+ end
end
def test_validates_length_of_with_allow_nil
@@ -684,10 +687,12 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_numericality_with_allow_nil_and_getter_method
- Developer.validates_numericality_of( :salary, :allow_nil => true)
- developer = Developer.new("name" => "michael", "salary" => nil)
- developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
- assert developer.valid?
+ repair_validations(Developer) do
+ Developer.validates_numericality_of( :salary, :allow_nil => true)
+ developer = Developer.new("name" => "michael", "salary" => nil)
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
+ assert developer.valid?
+ end
end
def test_validates_exclusion_of
@@ -892,26 +897,30 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_validates_size_of_association
- assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
- t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
- assert !t.save
- assert t.errors.on(:replies)
- reply = t.replies.build('title' => 'areply', 'content' => 'whateveragain')
- assert t.valid?
+ repair_validations(Owner) do
+ assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
+ o = Owner.new('name' => 'nopets')
+ assert !o.save
+ assert o.errors.on(:pets)
+ pet = o.pets.build('name' => 'apet')
+ assert o.valid?
+ end
end
def test_validates_size_of_association_using_within
- assert_nothing_raised { Topic.validates_size_of :replies, :within => 1..2 }
- t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
- assert !t.save
- assert t.errors.on(:replies)
-
- reply = t.replies.build('title' => 'areply', 'content' => 'whateveragain')
- assert t.valid?
-
- 2.times { t.replies.build('title' => 'areply', 'content' => 'whateveragain') }
- assert !t.save
- assert t.errors.on(:replies)
+ repair_validations(Owner) do
+ assert_nothing_raised { Owner.validates_size_of :pets, :within => 1..2 }
+ o = Owner.new('name' => 'nopets')
+ assert !o.save
+ assert o.errors.on(:pets)
+
+ pet = o.pets.build('name' => 'apet')
+ assert o.valid?
+
+ 2.times { o.pets.build('name' => 'apet') }
+ assert !o.save
+ assert o.errors.on(:pets)
+ end
end
def test_validates_length_of_nasty_params
@@ -1102,13 +1111,15 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_validates_size_of_association_utf8
- with_kcode('UTF8') do
- assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
- t = Topic.new('title' => 'ã‚ã„ã†ãˆãŠ', 'content' => 'ã‹ããã‘ã“')
- assert !t.save
- assert t.errors.on(:replies)
- t.replies.build('title' => 'ã‚ã„ã†ãˆãŠ', 'content' => 'ã‹ããã‘ã“')
- assert t.valid?
+ repair_validations(Owner) do
+ with_kcode('UTF8') do
+ assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
+ o = Owner.new('name' => 'ã‚ã„ã†ãˆãŠã‹ããã‘ã“')
+ assert !o.save
+ assert o.errors.on(:pets)
+ o.pets.build('name' => 'ã‚ã„ã†ãˆãŠã‹ããã‘ã“')
+ assert o.valid?
+ end
end
end
@@ -1127,14 +1138,16 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_validates_associated_one
- Reply.validates_associated( :topic )
- Topic.validates_presence_of( :content )
- r = Reply.new("title" => "A reply", "content" => "with content!")
- r.topic = Topic.create("title" => "uhohuhoh")
- assert !r.valid?
- assert r.errors.on(:topic)
- r.topic.content = "non-empty"
- assert r.valid?
+ repair_validations(Reply) do
+ Reply.validates_associated( :topic )
+ Topic.validates_presence_of( :content )
+ r = Reply.new("title" => "A reply", "content" => "with content!")
+ r.topic = Topic.create("title" => "uhohuhoh")
+ assert !r.valid?
+ assert r.errors.on(:topic)
+ r.topic.content = "non-empty"
+ assert r.valid?
+ end
end
def test_validate_block
@@ -1158,85 +1171,105 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_validates_acceptance_of_with_custom_error_using_quotes
- Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes"
- d = Developer.new
- d.salary = "0"
- assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
+ repair_validations(Developer) do
+ Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes"
+ d = Developer.new
+ d.salary = "0"
+ assert !d.valid?
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
+ end
end
def test_validates_confirmation_of_with_custom_error_using_quotes
- Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes"
- d = Developer.new
- d.name = "John"
- d.name_confirmation = "Johnny"
- assert !d.valid?
- assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name)
+ repair_validations(Developer) do
+ Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes"
+ d = Developer.new
+ d.name = "John"
+ d.name_confirmation = "Johnny"
+ assert !d.valid?
+ assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name)
+ end
end
def test_validates_format_of_with_custom_error_using_quotes
- Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes"
- d = Developer.new
- d.name = d.name_confirmation = "John 32"
- assert !d.valid?
- assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name)
+ repair_validations(Developer) do
+ Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes"
+ d = Developer.new
+ d.name = d.name_confirmation = "John 32"
+ assert !d.valid?
+ assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name)
+ end
end
def test_validates_inclusion_of_with_custom_error_using_quotes
- Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes"
- d = Developer.new
- d.salary = "90,000"
- assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
+ repair_validations(Developer) do
+ Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes"
+ d = Developer.new
+ d.salary = "90,000"
+ assert !d.valid?
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
+ end
end
def test_validates_length_of_with_custom_too_long_using_quotes
- Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes"
- d = Developer.new
- d.name = "Jeffrey"
- assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
+ repair_validations(Developer) do
+ Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes"
+ d = Developer.new
+ d.name = "Jeffrey"
+ assert !d.valid?
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
+ end
end
def test_validates_length_of_with_custom_too_short_using_quotes
- Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes"
- d = Developer.new
- d.name = "Joe"
- assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
+ repair_validations(Developer) do
+ Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes"
+ d = Developer.new
+ d.name = "Joe"
+ assert !d.valid?
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
+ end
end
def test_validates_length_of_with_custom_message_using_quotes
- Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes"
- d = Developer.new
- d.name = "Joe"
- assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
+ repair_validations(Developer) do
+ Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes"
+ d = Developer.new
+ d.name = "Joe"
+ assert !d.valid?
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
+ end
end
def test_validates_presence_of_with_custom_message_using_quotes
- Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes"
- d = Developer.new
- d.name = "Joe"
- assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent)
+ repair_validations(Developer) do
+ Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes"
+ d = Developer.new
+ d.name = "Joe"
+ assert !d.valid?
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent)
+ end
end
def test_validates_uniqueness_of_with_custom_message_using_quotes
- Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes"
- d = Developer.new
- d.name = "David"
- assert !d.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
+ repair_validations(Developer) do
+ Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes"
+ d = Developer.new
+ d.name = "David"
+ assert !d.valid?
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
+ end
end
def test_validates_associated_with_custom_message_using_quotes
- Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
- Topic.validates_presence_of :content
- r = Reply.create("title" => "A reply", "content" => "with content!")
- r.topic = Topic.create("title" => "uhohuhoh")
- assert !r.valid?
- assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).last
+ repair_validations(Reply) do
+ Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
+ Topic.validates_presence_of :content
+ r = Reply.create("title" => "A reply", "content" => "with content!")
+ r.topic = Topic.create("title" => "uhohuhoh")
+ assert !r.valid?
+ assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic)
+ end
end
def test_if_validation_using_method_true
@@ -1346,13 +1379,15 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_validates_associated_missing
- Reply.validates_presence_of(:topic)
- r = Reply.create("title" => "A reply", "content" => "with content!")
- assert !r.valid?
- assert r.errors.on(:topic)
-
- r.topic = Topic.find :first
- assert r.valid?
+ repair_validations(Reply) do
+ Reply.validates_presence_of(:topic)
+ r = Reply.create("title" => "A reply", "content" => "with content!")
+ assert !r.valid?
+ assert r.errors.on(:topic)
+
+ r.topic = Topic.find :first
+ assert r.valid?
+ end
end
def test_errors_to_xml
@@ -1364,14 +1399,14 @@ class ValidationsTest < ActiveRecord::TestCase
assert xml.include?("<error>Content Empty</error>")
end
- def test_validation_order
- Topic.validates_presence_of :title
- Topic.validates_length_of :title, :minimum => 2
+ def test_validation_order
+ Topic.validates_presence_of :title
+ Topic.validates_length_of :title, :minimum => 2
- t = Topic.new("title" => "")
- assert !t.valid?
- assert_equal "can't be blank", t.errors.on("title").first
- end
+ t = Topic.new("title" => "")
+ assert !t.valid?
+ assert_equal "can't be blank", t.errors.on("title").first
+ end
# previous implementation of validates_presence_of eval'd the
# string with the wrong binding, this regression test is to
@@ -1423,11 +1458,7 @@ class ValidatesNumericalityTest < ActiveRecord::TestCase
JUNK = ["not a number", "42 not a number", "0xdeadbeef", "00-1", "--3", "+-3", "+3-1", "-+019.0", "12.12.13.12", "123\nnot a number"]
INFINITY = [1.0/0.0]
- def setup
- Topic.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- Topic.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- Topic.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- end
+ repair_validations(Topic)
def test_default_validates_numericality_of
Topic.validates_numericality_of :approved
diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb
index 63f48865cc..39c6ea820d 100644
--- a/activerecord/test/cases/xml_serialization_test.rb
+++ b/activerecord/test/cases/xml_serialization_test.rb
@@ -31,6 +31,13 @@ class XmlSerializationTest < ActiveRecord::TestCase
assert_match %r{<created_at}, @xml
end
+ def test_should_allow_camelized_tags
+ @xml = Contact.new.to_xml :root => 'xml_contact', :camelize => true
+ assert_match %r{^<XmlContact>}, @xml
+ assert_match %r{</XmlContact>$}, @xml
+ assert_match %r{<CreatedAt}, @xml
+ end
+
def test_should_include_yielded_additions
@xml = Contact.new.to_xml do |xml|
xml.creator "David"
diff --git a/activerecord/test/fixtures/member_types.yml b/activerecord/test/fixtures/member_types.yml
new file mode 100644
index 0000000000..797a57430c
--- /dev/null
+++ b/activerecord/test/fixtures/member_types.yml
@@ -0,0 +1,6 @@
+founding:
+ id: 1
+ name: Founding
+provisional:
+ id: 2
+ name: Provisional
diff --git a/activerecord/test/fixtures/members.yml b/activerecord/test/fixtures/members.yml
index 67a6cc459a..6db945e61d 100644
--- a/activerecord/test/fixtures/members.yml
+++ b/activerecord/test/fixtures/members.yml
@@ -1,4 +1,6 @@
groucho:
name: Groucho Marx
+ member_type_id: 1
some_other_guy:
- name: Englebert Humperdink \ No newline at end of file
+ name: Englebert Humperdink
+ member_type_id: 2
diff --git a/activerecord/test/fixtures/people.yml b/activerecord/test/fixtures/people.yml
index d5a69e561d..3babb1fe59 100644
--- a/activerecord/test/fixtures/people.yml
+++ b/activerecord/test/fixtures/people.yml
@@ -1,6 +1,15 @@
michael:
id: 1
first_name: Michael
+ primary_contact_id: 2
+ gender: M
david:
id: 2
- first_name: David \ No newline at end of file
+ first_name: David
+ primary_contact_id: 3
+ gender: M
+susan:
+ id: 3
+ first_name: Susan
+ primary_contact_id: 2
+ gender: F \ No newline at end of file
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index 0e3fafa37c..3b27a9e272 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -80,6 +80,7 @@ class ExclusivelyDependentFirm < Company
has_one :account, :foreign_key => "firm_id", :dependent => :delete
has_many :dependent_sanitized_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => "name = 'BigShot Inc.'"
has_many :dependent_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => ["name = ?", 'BigShot Inc.']
+ has_many :dependent_hash_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => {:name => 'BigShot Inc.'}
end
class Client < Company
diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb
index 77a37abb38..255fb569d7 100644
--- a/activerecord/test/models/member.rb
+++ b/activerecord/test/models/member.rb
@@ -8,4 +8,5 @@ class Member < ActiveRecord::Base
has_one :sponsor_club, :through => :sponsor
has_one :member_detail
has_one :organization, :through => :member_detail
+ belongs_to :member_type
end \ No newline at end of file
diff --git a/activerecord/test/models/member_detail.rb b/activerecord/test/models/member_detail.rb
index e731454556..94f59e5794 100644
--- a/activerecord/test/models/member_detail.rb
+++ b/activerecord/test/models/member_detail.rb
@@ -1,4 +1,5 @@
class MemberDetail < ActiveRecord::Base
belongs_to :member
belongs_to :organization
+ has_one :member_type, :through => :member
end
diff --git a/activerecord/test/models/member_type.rb b/activerecord/test/models/member_type.rb
new file mode 100644
index 0000000000..a13561c72a
--- /dev/null
+++ b/activerecord/test/models/member_type.rb
@@ -0,0 +1,3 @@
+class MemberType < ActiveRecord::Base
+ has_many :members
+end
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index 430d0b38f7..ec2f684a6e 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -7,4 +7,10 @@ class Person < ActiveRecord::Base
has_many :jobs, :through => :references
has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true]
has_many :posts_with_comments_sorted_by_comment_id, :through => :readers, :source => :post, :include => :comments, :order => 'comments.id'
+
+ belongs_to :primary_contact, :class_name => 'Person'
+ has_many :agents, :class_name => 'Person', :foreign_key => 'primary_contact_id'
+
+ named_scope :males, :conditions => { :gender => 'M' }
+ named_scope :females, :conditions => { :gender => 'F' }
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 6217e3bc1c..8199cb8fc7 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -195,6 +195,7 @@ ActiveRecord::Schema.define do
create_table :members, :force => true do |t|
t.string :name
+ t.integer :member_type_id
end
create_table :member_details, :force => true do |t|
@@ -210,6 +211,10 @@ ActiveRecord::Schema.define do
t.string :type
end
+ create_table :member_types, :force => true do |t|
+ t.string :name
+ end
+
create_table :references, :force => true do |t|
t.integer :person_id
t.integer :job_id
@@ -293,8 +298,10 @@ ActiveRecord::Schema.define do
end
create_table :people, :force => true do |t|
- t.string :first_name, :null => false
- t.integer :lock_version, :null => false, :default => 0
+ t.string :first_name, :null => false
+ t.references :primary_contact
+ t.string :gender, :limit => 1
+ t.integer :lock_version, :null => false, :default => 0
end
create_table :pets, :primary_key => :pet_id ,:force => true do |t|
diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG
index e05a634a12..428c6d91e9 100644
--- a/activeresource/CHANGELOG
+++ b/activeresource/CHANGELOG
@@ -20,15 +20,15 @@
* Fixed that to_param should be used and honored instead of hardcoding the id #11406 [gspiers]
-* Improve documentation. [Radar, Jan De Poorter, chuyeow, xaviershay, danger, miloops, Xavier Noria, Sunny Ripert]
+* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert]
* Use HEAD instead of GET in exists? [bscofield]
-* Fix small documentation typo. Closes #10670 [l.guidi]
+* Fix small documentation typo. Closes #10670 [Luca Guidi]
* find_or_create_resource_for handles module nesting. #10646 [xavier]
-* Allow setting ActiveResource::Base#format before #site. [rick]
+* Allow setting ActiveResource::Base#format before #site. [Rick Olson]
* Support agnostic formats when calling custom methods. Closes #10635 [joerichsen]
@@ -48,9 +48,9 @@
* Don't cache net/http object so that ActiveResource is more thread-safe. Closes #10142 [kou]
-* Update XML documentation examples to include explicit type attributes. Closes #9754 [hasmanyjosh]
+* Update XML documentation examples to include explicit type attributes. Closes #9754 [Josh Susser]
-* Added one-off declarations of mock behavior [DHH]. Example:
+* Added one-off declarations of mock behavior [David Heinemeier Hansson]. Example:
Before:
ActiveResource::HttpMock.respond_to do |mock|
@@ -60,7 +60,7 @@
Now:
ActiveResource::HttpMock.respond_to.get "/people/1.xml", {}, "<person><name>David</name></person>"
-* Added ActiveResource.format= which defaults to :xml but can also be set to :json [DHH]. Example:
+* Added ActiveResource.format= which defaults to :xml but can also be set to :json [David Heinemeier Hansson]. Example:
class Person < ActiveResource::Base
self.site = "http://app/"
@@ -81,7 +81,7 @@
* Fix query methods on resources. [Cody Fauser]
-* pass the prefix_options to the instantiated record when using find without a specific id. Closes #8544 [alloy]
+* pass the prefix_options to the instantiated record when using find without a specific id. Closes #8544 [Eloy Duran]
* Recognize and raise an exception on 405 Method Not Allowed responses. #7692 [Josh Peek]
@@ -89,15 +89,15 @@
Comment.find(:all, :params => { :article_id => 5, :page => 2 }) or Comment.find(:all, :params => { 'article_id' => 5, :page => 2 })
-* Added find-one with symbol [DHH]. Example: Person.find(:one, :from => :leader) # => GET /people/leader.xml
+* Added find-one with symbol [David Heinemeier Hansson]. Example: Person.find(:one, :from => :leader) # => GET /people/leader.xml
-* BACKWARDS INCOMPATIBLE: Changed the finder API to be more extensible with :params and more strict usage of scopes [DHH]. Changes:
+* BACKWARDS INCOMPATIBLE: Changed the finder API to be more extensible with :params and more strict usage of scopes [David Heinemeier Hansson]. Changes:
Person.find(:all, :title => "CEO") ...becomes: Person.find(:all, :params => { :title => "CEO" })
Person.find(:managers) ...becomes: Person.find(:all, :from => :managers)
Person.find("/companies/1/manager.xml") ...becomes: Person.find(:one, :from => "/companies/1/manager.xml")
-* Add support for setting custom headers per Active Resource model [Rick]
+* Add support for setting custom headers per Active Resource model [Rick Olson]
class Project
headers['X-Token'] = 'foo'
@@ -106,13 +106,13 @@
# makes the GET request with the custom X-Token header
Project.find(:all)
-* Added find-by-path options to ActiveResource::Base.find [DHH]. Examples:
+* Added find-by-path options to ActiveResource::Base.find [David Heinemeier Hansson]. Examples:
employees = Person.find(:all, :from => "/companies/1/people.xml") # => GET /companies/1/people.xml
manager = Person.find("/companies/1/manager.xml") # => GET /companies/1/manager.xml
-* Added support for using classes from within a single nested module [DHH]. Example:
+* Added support for using classes from within a single nested module [David Heinemeier Hansson]. Example:
module Highrise
class Note < ActiveResource::Base
@@ -127,7 +127,7 @@
assert_kind_of Highrise::Comment, Note.find(1).comments.first
-* Added load_attributes_from_response as a way of loading attributes from other responses than just create [DHH]
+* Added load_attributes_from_response as a way of loading attributes from other responses than just create [David Heinemeier Hansson]
class Highrise::Task < ActiveResource::Base
def complete
@@ -143,18 +143,18 @@
Person.find(:managers) # => GET /people/managers.xml
Kase.find(1).post(:close) # => POST /kases/1/close.xml
-* Remove explicit prefix_options parameter for ActiveResource::Base#initialize. [Rick]
+* Remove explicit prefix_options parameter for ActiveResource::Base#initialize. [Rick Olson]
ActiveResource splits the prefix_options from it automatically.
-* Allow ActiveResource::Base.delete with custom prefix. [Rick]
+* Allow ActiveResource::Base.delete with custom prefix. [Rick Olson]
-* Add ActiveResource::Base#dup [Rick]
+* Add ActiveResource::Base#dup [Rick Olson]
-* Fixed constant warning when fetching the same object multiple times [DHH]
+* Fixed constant warning when fetching the same object multiple times [David Heinemeier Hansson]
-* Added that saves which get a body response (and not just a 201) will use that response to update themselves [DHH]
+* Added that saves which get a body response (and not just a 201) will use that response to update themselves [David Heinemeier Hansson]
-* Disregard namespaces from the default element name, so Highrise::Person will just try to fetch from "/people", not "/highrise/people" [DHH]
+* Disregard namespaces from the default element name, so Highrise::Person will just try to fetch from "/people", not "/highrise/people" [David Heinemeier Hansson]
* Allow array and hash query parameters. #7756 [Greg Spurrier]
@@ -176,7 +176,7 @@
* Base#==, eql?, and hash methods. == returns true if its argument is identical to self or if it's an instance of the same class, is not new?, and has the same id. eql? is an alias for ==. hash delegates to id. [Jeremy Kemper]
-* Allow subclassed resources to share the site info [Rick, Jeremy Kemper]
+* Allow subclassed resources to share the site info [Rick Olson, Jeremy Kemper]
d
class BeastResource < ActiveResource::Base
self.site = 'http://beast.caboo.se'
@@ -259,4 +259,4 @@ d
* Base.site= accepts URIs. 200...400 are valid response codes. PUT and POST request bodies default to ''. [Jeremy Kemper]
-* Initial checkin: object-oriented client for restful HTTP resources which follow the Rails convention. [DHH]
+* Initial checkin: object-oriented client for restful HTTP resources which follow the Rails convention. [David Heinemeier Hansson]
diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb
index 273fee3286..85103b53c5 100644
--- a/activeresource/lib/active_resource/connection.rb
+++ b/activeresource/lib/active_resource/connection.rb
@@ -146,8 +146,8 @@ module ActiveResource
def request(method, path, *arguments)
logger.info "#{method.to_s.upcase} #{site.scheme}://#{site.host}:#{site.port}#{path}" if logger
result = nil
- time = Benchmark.realtime { result = http.send(method, path, *arguments) }
- logger.info "--> %d %s (%d %.2fs)" % [result.code, result.message, result.body ? result.body.length : 0, time] if logger
+ ms = Benchmark.ms { result = http.send(method, path, *arguments) }
+ logger.info "--> %d %s (%d %.0fms)" % [result.code, result.message, result.body ? result.body.length : 0, ms] if logger
handle_response(result)
rescue Timeout::Error => e
raise TimeoutError.new(e.message)
diff --git a/activeresource/lib/active_resource/http_mock.rb b/activeresource/lib/active_resource/http_mock.rb
index 9ed532b48c..0b4549f759 100644
--- a/activeresource/lib/active_resource/http_mock.rb
+++ b/activeresource/lib/active_resource/http_mock.rb
@@ -54,6 +54,9 @@ module ActiveResource
end
for method in [ :post, :put, :get, :delete, :head ]
+ # def post(path, request_headers = {}, body = nil, status = 200, response_headers = {})
+ # @responses[Request.new(:post, path, nil, request_headers)] = Response.new(body || "", status, response_headers)
+ # end
module_eval <<-EOE, __FILE__, __LINE__
def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {})
@responses[Request.new(:#{method}, path, nil, request_headers)] = Response.new(body || "", status, response_headers)
@@ -118,6 +121,11 @@ module ActiveResource
end
for method in [ :post, :put ]
+ # def post(path, body, headers)
+ # request = ActiveResource::Request.new(:post, path, body, headers)
+ # self.class.requests << request
+ # self.class.responses[request] || raise(InvalidRequestError.new("No response recorded for #{request}"))
+ # end
module_eval <<-EOE, __FILE__, __LINE__
def #{method}(path, body, headers)
request = ActiveResource::Request.new(:#{method}, path, body, headers)
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 0e796d802c..757cb1da04 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,14 +1,27 @@
*2.3.0 [Edge]*
+* Object#tap shim for Ruby < 1.8.7. Similar to Object#returning, tap yields self then returns self. [Jeremy Kemper]
+ array.select { ... }.tap(&:inspect).map { ... }
+
+* TimeWithZone#- gives correct result with wrapped DateTime, and with DateTime argument [Geoff Buesing]
+
+* Updated i18n gem to version 0.1.1 #1635 [Yaroslav Markin]
+
+* Add :allow_nil option to delegate. #1127 [Sergio Gil]
+
+* Add Benchmark.ms convenience method to benchmark realtime in milliseconds. [Jeremy Kemper]
+
+* Updated included memcache-client to the 1.5.0.5 version which includes fixes from fiveruns and 37signals to deal with failover and timeouts #1535 [Joshua Sierles]
+
* Multibyte: add multibyte-safe Chars#ord rather than falling back to String#ord. #1483 [Jason Cheow]
* I18n support for Array#to_sentence. Introduces support.array.words_connector, .two_words_connector, and .last_word_connector translation keys. #1397 [Akira Matsuda]
* Added ActiveSupport::OrderedHash#each_key and ActiveSupport::OrderedHash#each_value #1410 [Christoffer Sawicki]
-* Added ActiveSupport::MessageVerifier and MessageEncryptor to aid users who need to store signed and/or encrypted messages. [Koz]
+* Added ActiveSupport::MessageVerifier and MessageEncryptor to aid users who need to store signed and/or encrypted messages. [Michael Koziarski]
-* Added ActiveSupport::BacktraceCleaner to cut down on backtrace noise according to filters and silencers [DHH]
+* Added ActiveSupport::BacktraceCleaner to cut down on backtrace noise according to filters and silencers [David Heinemeier Hansson]
* Added Object#try. ( Taken from http://ozmm.org/posts/try.html ) [Chris Wanstrath]
@@ -27,7 +40,7 @@
* Fixed the option merging in Array#to_xml #1126 [Rudolf Gavlas]
-* Make I18n::Backend::Simple reload its translations in development mode [DHH/Sven Fuchs]
+* Make I18n::Backend::Simple reload its translations in development mode [David Heinemeier Hansson/Sven Fuchs]
*2.2.0 [RC1] (October 24th, 2008)*
@@ -38,7 +51,7 @@
* Time#advance recognizes fractional days and weeks. Deprecate Durations of fractional months and years #970 [Tom Lea]
-* Add ActiveSupport::Rescuable module abstracting ActionController::Base rescue_from features. [Norbert Crombach, Pratik]
+* Add ActiveSupport::Rescuable module abstracting ActionController::Base rescue_from features. [Norbert Crombach, Pratik Naik]
* Switch from String#chars to String#mb_chars for the unicode proxy. [Manfred Stienstra]
@@ -52,7 +65,7 @@
* Added Inflector#parameterize for easy slug generation ("Donald E. Knuth".parameterize => "donald-e-knuth") #713 [Matt Darby]
-* Changed cache benchmarking to be reported in milliseconds [DHH]
+* Changed cache benchmarking to be reported in milliseconds [David Heinemeier Hansson]
* Fix Ruby's Time marshaling bug in pre-1.9 versions of Ruby: utc instances are now correctly unmarshaled with a utc zone instead of the system local zone [#900 state:resolved] [Luca Guidi, Geoff Buesing]
@@ -68,7 +81,7 @@
* Move the test related core_ext stuff out of core_ext so it's only loaded by the test helpers. [Michael Koziarski]
-* Add Inflection rules for String#humanize. #535 [dcmanges]
+* Add Inflection rules for String#humanize. #535 [Dan Manges]
ActiveSupport::Inflector.inflections do |inflect|
inflect.human(/_cnt$/i, '\1_count')
@@ -80,19 +93,19 @@
* Added TimeZone #=~, to support matching zones by regex in time_zone_select. #195 [Ernie Miller]
-* Added Array#second through Array#fifth as aliases for Array#[1] through Array#[4] + Array#forty_two as alias for Array[41] [DHH]
+* Added Array#second through Array#fifth as aliases for Array#[1] through Array#[4] + Array#forty_two as alias for Array[41] [David Heinemeier Hansson]
* Added test/do declaration style testing to ActiveSupport::TestCase [DHH via Jay Fields]
-* Added Object#present? which is equivalent to !Object#blank? [DHH]
+* Added Object#present? which is equivalent to !Object#blank? [David Heinemeier Hansson]
-* Added Enumberable#many? to encapsulate collection.size > 1 [DHH/Damian Janowski]
+* Added Enumberable#many? to encapsulate collection.size > 1 [David Heinemeier Hansson/Damian Janowski]
* Add more standard Hash methods to ActiveSupport::OrderedHash [Steve Purcell]
* Namespace Inflector, Dependencies, OrderedOptions, and TimeZone under ActiveSupport [Josh Peek]
-* Added StringInquirer for doing things like StringInquirer.new("production").production? # => true and StringInquirer.new("production").development? # => false [DHH]
+* Added StringInquirer for doing things like StringInquirer.new("production").production? # => true and StringInquirer.new("production").development? # => false [David Heinemeier Hansson]
* Fixed Date#end_of_quarter to not blow up on May 31st [#289 state:resolved] (Danger)
@@ -129,7 +142,7 @@
* TimeWithZone respects config.active_support.use_standard_json_time_format [Geoff Buesing]
-* Add config.active_support.escape_html_entities_in_json to allow disabling of html entity escaping. [rick]
+* Add config.active_support.escape_html_entities_in_json to allow disabling of html entity escaping. [Rick Olson]
* Improve documentation. [Xavier Noria]
@@ -141,7 +154,7 @@
* TimeWithZone#method_missing: send to utc to advance with dst correctness, otherwise send to time. Adding tests for time calculations methods [Geoff Buesing]
-* Add config.active_support.use_standard_json_time_format setting so that Times and Dates export to ISO 8601 dates. [rick]
+* Add config.active_support.use_standard_json_time_format setting so that Times and Dates export to ISO 8601 dates. [Rick Olson]
* TZInfo: Removing unneeded TimezoneProxy class [Geoff Buesing]
@@ -159,9 +172,9 @@
* TimeWithZone#usec returns 0 instead of error when DateTime is wrapped [Geoff Buesing]
-* Improve documentation. [Radar, Jan De Poorter, chuyeow, xaviershay, danger, miloops, Xavier Noria, Sunny Ripert]
+* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert]
-* Ensure that TimeWithZone#to_yaml works when passed a YAML::Emitter. [rick]
+* Ensure that TimeWithZone#to_yaml works when passed a YAML::Emitter. [Rick Olson]
* Ensure correct TimeWithZone#to_date [Geoff Buesing]
@@ -193,7 +206,7 @@
* Adding TimeWithZone #marshal_dump and #marshal_load [Geoff Buesing]
-* Add OrderedHash#to_hash [josh]
+* Add OrderedHash#to_hash [Josh Peek]
* Adding Time#end_of_day, _quarter, _week, and _year. #9312 [Juanjo Bazan, Tarmo Tänav, BigTitus]
@@ -203,9 +216,9 @@
* TimeWithZone #+ and #- behave consistently with numeric arguments regardless of whether wrapped time is a Time or DateTime; consistenty answers false to #acts_like?(:date) [Geoff Buesing]
-* Add String#squish and String#squish! to remove consecutive chunks of whitespace. #11123 [jordi, Henrik N]
+* Add String#squish and String#squish! to remove consecutive chunks of whitespace. #11123 [Jordi Bunster, Henrik N]
-* Serialize BigDecimals as Floats when using to_yaml. #8746 [ernesto.jimenez]
+* Serialize BigDecimals as Floats when using to_yaml. #8746 [Ernesto Jimenez]
* Adding TimeWithZone #to_yaml, #to_datetime, #eql? and method aliases for duck-typing compatibility with Time [Geoff Buesing]
@@ -223,7 +236,7 @@
* TimeZone#new method renamed #local; when used with Time.zone, constructor now reads: Time.zone.local() [Geoff Buesing]
-* Added Base64.encode64s to encode values in base64 without the newlines. This makes the values immediately usable as URL parameters or memcache keys without further processing [DHH]
+* Added Base64.encode64s to encode values in base64 without the newlines. This makes the values immediately usable as URL parameters or memcache keys without further processing [David Heinemeier Hansson]
* Remove :nodoc: entries around the ActiveSupport test/unit assertions. #10946 [dancroak, jamesh]
@@ -231,7 +244,7 @@
* cache.fetch(key, :force => true) to force a cache miss. [Jeremy Kemper]
-* Support retrieving TimeZones with a Duration. TimeZone[-28800] == TimeZone[-480.minutes]. [rick]
+* Support retrieving TimeZones with a Duration. TimeZone[-28800] == TimeZone[-480.minutes]. [Rick Olson]
* TimeWithZone#- added, so that #- can handle a Time or TimeWithZone argument correctly [Geoff Buesing]
@@ -279,21 +292,21 @@
* TestCase: introduce declared setup and teardown callbacks. Pass a list of methods and an optional block to call before setup or after teardown. Setup callbacks are run in the order declared; teardown callbacks are run in reverse. [Jeremy Kemper]
-* Added ActiveSupport::Gzip.decompress/compress(source) as an easy wrapper for Zlib [Tobias Luetke]
+* Added ActiveSupport::Gzip.decompress/compress(source) as an easy wrapper for Zlib [Tobias Lütke]
* Included MemCache-Client to make the improved ActiveSupport::Cache::MemCacheStore work out of the box [Bob Cottrell, Eric Hodel]
-* Added ActiveSupport::Cache::* framework as an extraction from ActionController::Caching::Fragments::* [DHH]
+* Added ActiveSupport::Cache::* framework as an extraction from ActionController::Caching::Fragments::* [David Heinemeier Hansson]
* Fixed String#titleize to work for strings with 's too #10571 [trek]
-* Changed the implementation of Enumerable#group_by to use a double array approach instead of a hash such that the insert order is honored [DHH/Marcel]
+* Changed the implementation of Enumerable#group_by to use a double array approach instead of a hash such that the insert order is honored [David Heinemeier Hansson/Marcel Molina Jr.]
-* remove multiple enumerations from ActiveSupport::JSON#convert_json_to_yaml when dealing with date/time values. [rick]
+* remove multiple enumerations from ActiveSupport::JSON#convert_json_to_yaml when dealing with date/time values. [Rick Olson]
* Hash#symbolize_keys skips keys that can't be symbolized. #10500 [Brad Greenlee]
-* Ruby 1.9 compatibility. #1689, #10466, #10468, #10554, #10594, #10632 [Cheah Chu Yeow, Pratik Naik, Jeremy Kemper, Dirkjan Bussink, fxn]
+* Ruby 1.9 compatibility. #1689, #10466, #10468, #10554, #10594, #10632 [Cheah Chu Yeow, Pratik Naik, Jeremy Kemper, Dirkjan Bussink, Xavier Noria]
* TimeZone#to_s uses UTC rather than GMT. #1689 [Cheah Chu Yeow]
@@ -304,7 +317,7 @@
*2.0.1* (December 7th, 2007)
-* Added Array#from and Array#to that behaves just from String#from and String#to [DHH]
+* Added Array#from and Array#to that behaves just from String#from and String#to [David Heinemeier Hansson]
* Fix that empty collections should be treated as empty arrays regardless of whitespace for Hash#from_xml #10255 [adamj]
@@ -312,17 +325,17 @@
* Change Time and DateTime #end_of_month to return last second of month instead of beginning of last day of month. Closes #10200 [Geoff Buesing]
-* Speedup String#blank? [Jeremy Kemper, Koz]
+* Speedup String#blank? [Jeremy Kemper, Michael Koziarski]
* Add documentation for Hash#diff. Closes #9306 [Tarmo Tänav]
-* Add new superclass_delegating_accessors. Similar to class inheritable attributes but with subtly different semantics. [Koz, Tarmo Tänav]
+* Add new superclass_delegating_accessors. Similar to class inheritable attributes but with subtly different semantics. [Michael Koziarski, Tarmo Tänav]
* Change JSON to encode %w(< > &) as 4 digit hex codes to be in compliance with the JSON spec. Closes #9975 [Josh Peek, Cheah Chu Yeow, Tim Pope]
-* Fix JSON encoding/decoding bugs dealing with /'s. Closes #9990 [Rick, theamazingrando]
+* Fix JSON encoding/decoding bugs dealing with /'s. Closes #9990 [Rick Olson, theamazingrando]
-* Introduce a base class for all test cases used by rails applications. ActiveSupport::TestCase [Koz]
+* Introduce a base class for all test cases used by rails applications. ActiveSupport::TestCase [Michael Koziarski]
The intention is to use this to reduce the amount of monkeypatching / overriding that
is done to test/unit's classes.
@@ -375,13 +388,13 @@
* Backport Object#instance_variable_defined? for Ruby < 1.8.6. [Jeremy Kemper]
-* BufferedLogger#add converts the message to a string. #9702, #9724 [eigentone, DrMark, tomafro]
+* BufferedLogger#add converts the message to a string. #9702, #9724 [eigentone, DrMark, Tom Ward]
-* Added ActiveSupport::BufferedLogger as a duck-typing alternative (albeit with no formatter) to the Ruby Logger, which provides a very nice speed bump (inspired by Ezra's buffered logger) [DHH]
+* Added ActiveSupport::BufferedLogger as a duck-typing alternative (albeit with no formatter) to the Ruby Logger, which provides a very nice speed bump (inspired by Ezra's buffered logger) [David Heinemeier Hansson]
* Object#instance_exec produces fewer garbage methods. [Mauricio Fernandez]
-* Decode json strings as Dates/Times if they're using a YAML-compatible format. Closes #9614 [Rick]
+* Decode json strings as Dates/Times if they're using a YAML-compatible format. Closes #9614 [Rick Olson]
* Fixed cache_page to use the request url instead of the routing options when picking a save path. #8614 [Josh Peek]
@@ -389,11 +402,11 @@
* Fixed that pluralizing an empty string should return the same empty string, not "s". #7720 [Josh Peek]
-* Added call to inspect on non-string classes for the logger #8533 [codahale]
+* Added call to inspect on non-string classes for the logger #8533 [Coda Hale]
* Deprecation: remove deprecated :mday option from Time, Date, and DateTime#change. [Jeremy Kemper]
-* Fix JSON decoder with nested quotes and commas. #9579 [zdennis]
+* Fix JSON decoder with nested quotes and commas. #9579 [Zach Dennis]
* Hash#to_xml doesn't double-unescape. #8806 [Ezran]
@@ -405,27 +418,27 @@
* Deprecation: removed Reloadable. [Jeremy Kemper]
-* Make the utf-handler return the correct value for non-matching regular expressions. Closes #9049 [manfred]
+* Make the utf-handler return the correct value for non-matching regular expressions. Closes #9049 [Manfred Stienstra]
-* Add ljust, rjust and center to utf8-handler. Closes #9165 [manfred]
+* Add ljust, rjust and center to utf8-handler. Closes #9165 [Manfred Stienstra]
-* Fix Time#advance bug when trying to advance a year from leap day. Closes #8655 [gbuesing]
+* Fix Time#advance bug when trying to advance a year from leap day. Closes #8655 [Geoff Buesing]
-* Add support for []= on ActiveSupport::Multibyte::Chars. Closes #9142. [ewan, manfred]
+* Add support for []= on ActiveSupport::Multibyte::Chars. Closes #9142. [ewan, Manfred Stienstra]
* Added Array#extract_options! to encapsulate the pattern of getting an options hash out of a variable number of parameters. #8759 [Norbert Crombach]
* Let alias_attribute work with attributes with initial capital letters (legacy columns etc). Closes #8596 [mpalmer]
-* Added Hash#except which is the inverse of Hash#slice -- return the hash except the keys that are specified [DHH]
+* Added Hash#except which is the inverse of Hash#slice -- return the hash except the keys that are specified [David Heinemeier Hansson]
-* Added support for pluralization with a different starting letter than the singular version (cow/kine) #4929 [norri_b/hasmanyjosh]
+* Added support for pluralization with a different starting letter than the singular version (cow/kine) #4929 [norri_b/Josh Susser]
* Demote Hash#to_xml to use XmlSimple#xml_in_string so it can't read files or stdin. #8453 [candlerb, Jeremy Kemper]
-* Backport clean_logger changes to support ruby 1.8.2 [mislav]
+* Backport clean_logger changes to support ruby 1.8.2 [Mislav Marohnić]
-* Added proper handling of arrays #8537 [hasmanyjosh]
+* Added proper handling of arrays #8537 [Josh Susser]
Before:
Hash.from_xml '<images></images>'
@@ -459,7 +472,7 @@
* Move common DateTime calculations to Date. #8536 [Geoff Buesing]
-* Added Date#change (like Time#change) [DHH]
+* Added Date#change (like Time#change) [David Heinemeier Hansson]
* DateTime#to_time converts to Time unless out of range. #8512 [Geoff Buesing]
@@ -467,7 +480,7 @@
* Time durations use since instead of + for accuracy. #8513 [Geoff Buesing]
-* escape <'s and >'s in JSON strings. #8371 [Rick]
+* escape <'s and >'s in JSON strings. #8371 [Rick Olson]
* Inflections: MatrixTest -> MatrixTests instead of MatricesTest. #8496 [jbwiv]
@@ -483,11 +496,11 @@
* Simplify API of assert_difference by passing in an expression that is evaluated before and after the passed in block. See documenation for examples of new API. [Marcel Molina Jr.]
-* Added assert_difference and assert_no_difference to test/unit assertions [Tobias Luetke]
+* Added assert_difference and assert_no_difference to test/unit assertions [Tobias Lütke]
-* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back [DHH]
+* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back [David Heinemeier Hansson]
-* Added parsing of file type in Hash.xml_in so you can easily do file uploads with base64 from an API [DHH]
+* Added parsing of file type in Hash.xml_in so you can easily do file uploads with base64 from an API [David Heinemeier Hansson]
<person>
<name>David</name>
@@ -507,9 +520,9 @@
* Use XSD-compatible type names for Hash#to_xml and make the converters extendable #8047 [Tim Pope]
-* Added yielding of builder in Hash#to_xml [DHH]
+* Added yielding of builder in Hash#to_xml [David Heinemeier Hansson]
-* Hash#with_indifferent_access now also converts hashes kept in arrays to indifferent access (makes it easier to treat HTML and XML parameters the same) [DHH]
+* Hash#with_indifferent_access now also converts hashes kept in arrays to indifferent access (makes it easier to treat HTML and XML parameters the same) [David Heinemeier Hansson]
* Hash#to_xml supports YAML attributes. #7502 [jonathan]
@@ -527,9 +540,9 @@
* Give DateTime correct .to_s implementations, lets it play nice with ActiveRecord quoting. #7649 [Geoff Buesing]
-* Add File.atomic_write, allows you to write large files in an atomic manner, preventing users from seeing half written files. [Koz]
+* Add File.atomic_write, allows you to write large files in an atomic manner, preventing users from seeing half written files. [Michael Koziarski]
-* Allow users to provide custom formatters to Logger. [aeden]
+* Allow users to provide custom formatters to Logger. [Anthony Eden]
* Hash#to_query CGI-escapes its keys. [Jeremy Kemper]
@@ -538,7 +551,7 @@
* :db format for Date#to_s [Jeremy Kemper]
Date.new(2007, 1, 27).to_s(:db) # => '2007-01-27'
-* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. [Rick]
+* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. [Rick Olson]
* Added Hash#to_query to turn a hash of values into a form-encoded query string [Nicholas Seckar]
@@ -573,7 +586,7 @@ public for compatibility. [Jeremy Kemper]
* Optimize Class Inheritable Attributes so that unnecessary hashes are not created. Closes #7472 [Bruce Perens]
-* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. [Rick]
+* Added :instance_writer option to #mattr_writer/accessor, #cattr_writer/accessor, and #class_inheritable_writer to skip the creation of the instance writer. [Rick Olson]
* Full test coverage for Inflector. #7228 [Dan Kubb]
@@ -598,13 +611,13 @@ public for compatibility. [Jeremy Kemper]
* Fixed Array#to_xml when it contains a series of hashes (each piece would get its own XML declaration) #6610 [thkarcher/cyu]
-* Added Time#to_s(:time) which will just return H:M, like 17:44 [DHH]
+* Added Time#to_s(:time) which will just return H:M, like 17:44 [David Heinemeier Hansson]
* Add Module#attr_accessor_with_default to initialize value of attribute before setting it. Closes #6538. [Stuart Halloway, Marcel Molina Jr.]
-* Hash#to_xml handles keys with the same name as Kernel methods. #6613 [Catfish]
+* Hash#to_xml handles keys with the same name as Kernel methods. #6613 [Jonathan del Strother]
-* Added Time#end_of_day to get 23:59:59 of that day [DHH]
+* Added Time#end_of_day to get 23:59:59 of that day [David Heinemeier Hansson]
* Don't quote hash keys in Hash#to_json if they're valid JavaScript identifiers. Disable this with ActiveSupport::JSON.unquote_hash_key_identifiers = false if you need strict JSON compliance. [Sam Stephenson]
@@ -614,11 +627,11 @@ public for compatibility. [Jeremy Kemper]
* Fix unicode JSON regexp for Onigurama compatibility. #6494 [whitley]
-* update XmlSimple to 1.0.10. Closes #6532. [nicksieger]
+* update XmlSimple to 1.0.10. Closes #6532. [Nick Sieger]
* Update dependencies to allow constants to be defined alongside their siblings. A common case for this is AR model classes with STI; user.rb might define User, Administrator and Guest for example. [Nicholas Seckar]
-* next_week respects DST changes. #6483, #5617, #2353, #2509, #4551 [marclove, rabiedenharn, rails@roetzel.de, jsolson@damogran.org, drbrain@segment7.net]
+* next_week respects DST changes. #6483, #5617, #2353, #2509, #4551 [marclove, Rob Biedenharn, rails@roetzel.de, jsolson@damogran.org, drbrain@segment7.net]
* Expose methods added to Enumerable in the documentation, such as group_by. Closes #6170. [sergeykojin@gmail.com, Marcel Molina Jr.]
@@ -638,9 +651,9 @@ public for compatibility. [Jeremy Kemper]
* Hash#to_xml supports Bignum and BigDecimal. #6313 [edibiase]
-* Don't undefine #class in OptionMerger [Rick]
+* Don't undefine #class in OptionMerger [Rick Olson]
-* Hash.create_from_xml has been renamed to Hash.from_xml, alias will exist until Rails 2.0 [DHH]
+* Hash.create_from_xml has been renamed to Hash.from_xml, alias will exist until Rails 2.0 [David Heinemeier Hansson]
* alias_method_chain works with accessor= methods also. #6153 [Caio Chassot]
@@ -692,13 +705,13 @@ public for compatibility. [Jeremy Kemper]
self.attr_internal_naming_format = '@%s__rofl'
attr_internal :foo
-* Raise fully qualified names upon name errors. #5533 [lars@pinds.com, Nicholas Seckar]
+* Raise fully qualified names upon name errors. #5533 [Lars Pind, Nicholas Seckar]
* Add extention to obtain the missing constant from NameError instances. [Nicholas Seckar]
* Thoroughly document inflections. #5700 [petermichaux@gmail.com]
-* Added Module#alias_attribute [Jamis/DHH]. Example:
+* Added Module#alias_attribute [Jamis/David Heinemeier Hansson]. Example:
class Content < ActiveRecord::Base
# has a title attribute
@@ -720,23 +733,23 @@ public for compatibility. [Jeremy Kemper]
Provide your own per-environment in e.g. config/environments/development.rb:
ActiveSupport::Deprecation.behavior = Proc.new { |message| raise message }
-* First cut of the Rails Deprecation system. [Koz]
+* First cut of the Rails Deprecation system. [Michael Koziarski]
* Strip boolean XML content before checking for 'true' [Rick Olson]
-* Customize default BigDecimal formatting. References #5672 [dave@pragprog.com]
+* Customize default BigDecimal formatting. References #5672 [Dave Thomas]
-* Correctly convert <foo nil="true"> to nil when using Hash.create_from_xml. [Rick]
+* Correctly convert <foo nil="true"> to nil when using Hash.create_from_xml. [Rick Olson]
* Optional identity for Enumerable#sum defaults to zero. #5657 [gensym@mac.com]
-* HashWithIndifferentAccess shouldn't confuse false and nil. #5601 [shugo@ruby-lang.org]
+* HashWithIndifferentAccess shouldn't confuse false and nil. #5601 [Shugo Maeda]
* Fixed HashWithIndifferentAccess#default #5586 [chris@seagul.co.uk]
* More compatible Hash.create_from_xml. #5523 [nunemaker@gmail.com]
-* Added Enumerable#sum for calculating a sum from the elements [DHH, jonathan@daikini.com]. Examples:
+* Added Enumerable#sum for calculating a sum from the elements [David Heinemeier Hansson, jonathan@daikini.com]. Examples:
[1, 2, 3].sum
payments.sum { |p| p.price * p.tax_rate }
@@ -758,7 +771,7 @@ public for compatibility. [Jeremy Kemper]
{1 => "one", 2 => "two", 3 => "three"}.sort_by(&:first).map(&:last)
#=> ["one", "two", "three"]
-* Added Hash.create_from_xml(string) which will create a hash from a XML string and even typecast if possible [DHH]. Example:
+* Added Hash.create_from_xml(string) which will create a hash from a XML string and even typecast if possible [David Heinemeier Hansson]. Example:
Hash.create_from_xml <<-EOT
<note>
@@ -771,7 +784,7 @@ public for compatibility. [Jeremy Kemper]
{ :note => { :title => "This is a note", :created_at => Date.new(2004, 10, 10) } }
-* Added Jim Weirich's excellent FlexMock class to vendor (Copyright 2003, 2004 by Jim Weirich (jim@weriichhouse.org)) -- it's not automatically required, though, so require 'flexmock' is still necessary [DHH]
+* Added Jim Weirich's excellent FlexMock class to vendor (Copyright 2003, 2004 by Jim Weirich (jim@weriichhouse.org)) -- it's not automatically required, though, so require 'flexmock' is still necessary [David Heinemeier Hansson]
* Fixed that Module#alias_method_chain should work with both foo? foo! and foo at the same time #4954 [anna@wota.jp]
@@ -779,13 +792,13 @@ public for compatibility. [Jeremy Kemper]
* Add OrderedHash#values. [Sam Stephenson]
-* Added Array#to_s(:db) that'll produce a comma-separated list of ids [DHH]. Example:
+* Added Array#to_s(:db) that'll produce a comma-separated list of ids [David Heinemeier Hansson]. Example:
Purchase.find(:all, :conditions => "product_id IN (#{shops.products.to_s(:db)})"
* Normalize classify's argument to a String so that it plays nice with Symbols. [Marcel Molina Jr.]
-* Strip out leading schema name in classify. References #5139. [schoenm@earthlink.net]
+* Strip out leading schema name in classify. References #5139. [Michael Schoen]
* Remove Enumerable#first_match since break(value) handles the use case well enough. [Nicholas Seckar]
@@ -817,7 +830,7 @@ public for compatibility. [Jeremy Kemper]
* Added Module#alias_method_chain [Jamis Buck]
-* Updated to Builder 2.0 [DHH]
+* Updated to Builder 2.0 [David Heinemeier Hansson]
* Add Array#split for dividing arrays into one or more subarrays by value or block. [Sam Stephenson]
@@ -848,7 +861,7 @@ public for compatibility. [Jeremy Kemper]
* Added Fixnum#seconds for consistency, so you can say 5.minutes + 30.seconds instead of 5.minutes + 30 #4389 [François Beausoleil]
-* Added option to String#camelize to generate lower-cased camel case by passing in :lower, like "super_man".camelize(:lower) # => "superMan" [DHH]
+* Added option to String#camelize to generate lower-cased camel case by passing in :lower, like "super_man".camelize(:lower) # => "superMan" [David Heinemeier Hansson]
* Added Hash#diff to show the difference between two hashes [Chris McGrath]
@@ -856,12 +869,12 @@ public for compatibility. [Jeremy Kemper]
* Enhance Inflector.underscore to convert '-' into '_' (as the inverse of Inflector.dasherize) [Jamis Buck]
-* Switched to_xml to use the xml schema format for datetimes. This allows the encoding of time zones and should improve operability. [Koz]
+* Switched to_xml to use the xml schema format for datetimes. This allows the encoding of time zones and should improve operability. [Michael Koziarski]
* Added a note to the documentation for the Date related Numeric extensions to indicate that they're
-approximations and shouldn't be used for critical calculations. [Koz]
+approximations and shouldn't be used for critical calculations. [Michael Koziarski]
-* Added Hash#to_xml and Array#to_xml that makes it much easier to produce XML from basic structures [DHH]. Examples:
+* Added Hash#to_xml and Array#to_xml that makes it much easier to produce XML from basic structures [David Heinemeier Hansson]. Examples:
{ :name => "David", :street_name => "Paulina", :age => 26, :moved_on => Date.new(2005, 11, 15) }.to_xml
@@ -874,7 +887,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
<moved-on type="date">2005-11-15</moved-on>
</person>
-* Moved Jim Weirich's wonderful Builder from Action Pack to Active Support (it's simply too useful to be stuck in AP) [DHH]
+* Moved Jim Weirich's wonderful Builder from Action Pack to Active Support (it's simply too useful to be stuck in AP) [David Heinemeier Hansson]
* Fixed that Array#to_sentence will return "" on an empty array instead of ", and" #3842, #4031 [rubyonrails@beautifulpixel.com]
@@ -903,7 +916,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
[Marcel Molina Jr., Sam Stephenson]
-* Added Kernel#daemonize to turn the current process into a daemon that can be killed with a TERM signal [DHH]
+* Added Kernel#daemonize to turn the current process into a daemon that can be killed with a TERM signal [David Heinemeier Hansson]
* Add 'around' methods to Logger, to make it easy to log before and after messages for a given block as requested in #3809. [Michael Koziarski] Example:
@@ -918,7 +931,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
* Make String#last return the string instead of nil when it is shorter than the limit [Scott Barron].
-* Added delegation support to Module that allows multiple delegations at once (unlike Forwardable in the stdlib) [DHH]. Example:
+* Added delegation support to Module that allows multiple delegations at once (unlike Forwardable in the stdlib) [David Heinemeier Hansson]. Example:
class Account < ActiveRecord::Base
has_one :subscription
@@ -953,7 +966,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
include Reloadable
end
- Reloading a class is done by removing its constant which will cause it to be loaded again on the next reference. [DHH]
+ Reloading a class is done by removing its constant which will cause it to be loaded again on the next reference. [David Heinemeier Hansson]
* Added auto-loading support for classes in modules, so Conductor::Migration will look for conductor/migration.rb and Conductor::Database::Settings will look for conductor/database/settings.rb [Nicholas Seckar]
@@ -961,7 +974,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
* Add Proc#bind(object) for changing a proc or block's self by returning a Method bound to the given object. Based on why the lucky stiff's "cloaker" method. [Sam Stephenson]
-* Fix merge and dup for hashes with indifferent access #3404 [kenneth.miller@bitfield.net]
+* Fix merge and dup for hashes with indifferent access #3404 [Ken Miller]
* Fix the requires in option_merger_test to unbreak AS tests. [Sam Stephenson]
@@ -1068,7 +1081,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
* Fixed clean logger to work with Ruby 1.8.3 Logger class #2245
-* Fixed memory leak with Active Record classes when Dependencies.mechanism = :load #1704 [c.r.mcgrath@gmail.com]
+* Fixed memory leak with Active Record classes when Dependencies.mechanism = :load #1704 [Chris McGrath]
* Fixed Inflector.underscore for use with acronyms, so HTML becomes html instead of htm_l #2173 [k@v2studio.com]
@@ -1078,11 +1091,11 @@ approximations and shouldn't be used for critical calculations. [Koz]
* Added Hash#reverse_merge, Hash#reverse_merge!, and Hash#reverse_update to ease the use of default options
-* Added Array#to_sentence that'll turn ['one', 'two', 'three'] into "one, two, and three" #2157 [m.stienstra@fngtps.com]
+* Added Array#to_sentence that'll turn ['one', 'two', 'three'] into "one, two, and three" #2157 [Manfred Stienstra]
* Added Kernel#silence_warnings to turn off warnings temporarily for the passed block
-* Added String#starts_with? and String#ends_with? #2118 [thijs@vandervossen.net]
+* Added String#starts_with? and String#ends_with? #2118 [Thijs van der Vossen]
* Added easy extendability to the inflector through Inflector.inflections (using the Inflector::Inflections singleton class). Examples:
@@ -1125,7 +1138,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
* Added new rules to the Inflector to deal with more unusual plurals mouse/louse => mice/lice, information => information, ox => oxen, virus => viri, archive => archives #1571, #1583, #1490, #1599, #1608 [foamdino@gmail.com/others]
-* Fixed memory leak with Object#remove_subclasses_of, which inflicted a Rails application running in development mode with a ~20KB leak per request #1289 [c.r.mcgrath@gmail.com]
+* Fixed memory leak with Object#remove_subclasses_of, which inflicted a Rails application running in development mode with a ~20KB leak per request #1289 [Chris McGrath]
* Made 1.year == 365.25.days to account for leap years. This allows you to do User.find(:all, :conditions => ['birthday > ?', 50.years.ago]) without losing a lot of days. #1488 [tuxie@dekadance.se]
@@ -1160,7 +1173,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
* Fixed that in some circumstances controllers outside of modules may have hidden ones inside modules. For example, admin/content might have been hidden by /content. #1075 [Nicholas Seckar]
-* Fixed inflection of perspectives and similar words #1045 [thijs@vandervossen.net]
+* Fixed inflection of perspectives and similar words #1045 [Thijs van der Vossen]
* Added Fixnum#even? and Fixnum#odd?
@@ -1202,7 +1215,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
* Added inflection rules for "sh" words, like "wish" and "fish" #755 [phillip@pjbsoftware.com]
-* Fixed an exception when using Ajax based requests from Safari because Safari appends a \000 to the post body. Symbols can't have \000 in them so indifferent access would throw an exception in the constructor. Indifferent hashes now use strings internally instead. #746 [Tobias Luetke]
+* Fixed an exception when using Ajax based requests from Safari because Safari appends a \000 to the post body. Symbols can't have \000 in them so indifferent access would throw an exception in the constructor. Indifferent hashes now use strings internally instead. #746 [Tobias Lütke]
* Added String#to_time and String#to_date for wrapping ParseDate
@@ -1268,7 +1281,7 @@ approximations and shouldn't be used for critical calculations. [Koz]
* Added Inflections as an extension on String, so Inflector.pluralize(Inflector.classify(name)) becomes name.classify.pluralize #476 [Jeremy Kemper]
-* Added Byte operations to Numeric, so 5.5.megabytes + 200.kilobytes #461 [Marcel Molina]
+* Added Byte operations to Numeric, so 5.5.megabytes + 200.kilobytes #461 [Marcel Molina Jr.]
* Fixed that Dependencies.reload can't load the same file twice #420 [Kent Sibilev]
diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb
index b2c863c893..33bcf327f8 100644
--- a/activesupport/lib/active_support/buffered_logger.rb
+++ b/activesupport/lib/active_support/buffered_logger.rb
@@ -68,13 +68,13 @@ module ActiveSupport
for severity in Severity.constants
class_eval <<-EOT, __FILE__, __LINE__
- def #{severity.downcase}(message = nil, progname = nil, &block)
- add(#{severity}, message, progname, &block)
- end
-
- def #{severity.downcase}?
- #{severity} >= @level
- end
+ def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block)
+ add(#{severity}, message, progname, &block) # add(DEBUG, message, progname, &block)
+ end # end
+ #
+ def #{severity.downcase}? # def debug?
+ #{severity} >= @level # DEBUG >= @level
+ end # end
EOT
end
@@ -96,9 +96,12 @@ module ActiveSupport
@guard.synchronize do
unless buffer.empty?
old_buffer = buffer
- clear_buffer
@log.write(old_buffer.join)
end
+
+ # Important to do this even if buffer was empty or else @buffer will
+ # accumulate empty arrays for each request where nothing was logged.
+ clear_buffer
end
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 10281d60eb..6a6c861458 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -143,13 +143,13 @@ module ActiveSupport
log("miss", key, options)
value = nil
- seconds = Benchmark.realtime { value = yield }
+ ms = Benchmark.ms { value = yield }
@logger_off = true
write(key, value, options)
@logger_off = false
- log("write (will save #{'%.2f' % (seconds * 1000)}ms)", key, nil)
+ log('write (will save %.2fms)' % ms, key, nil)
value
end
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 5cdcaf5ad1..86e66e0588 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -192,13 +192,8 @@ module ActiveSupport
end
def should_run_callback?(*args)
- if options[:if]
- evaluate_method(options[:if], *args)
- elsif options[:unless]
- !evaluate_method(options[:unless], *args)
- else
- true
- end
+ [options[:if]].flatten.compact.all? { |a| evaluate_method(a, *args) } &&
+ ![options[:unless]].flatten.compact.any? { |a| evaluate_method(a, *args) }
end
end
@@ -210,20 +205,24 @@ module ActiveSupport
def define_callbacks(*callbacks)
callbacks.each do |callback|
class_eval <<-"end_eval"
- def self.#{callback}(*methods, &block)
- callbacks = CallbackChain.build(:#{callback}, *methods, &block)
- (@#{callback}_callbacks ||= CallbackChain.new).concat callbacks
- end
-
- def self.#{callback}_callback_chain
- @#{callback}_callbacks ||= CallbackChain.new
-
- if superclass.respond_to?(:#{callback}_callback_chain)
- CallbackChain.new(superclass.#{callback}_callback_chain + @#{callback}_callbacks)
- else
- @#{callback}_callbacks
- end
- end
+ def self.#{callback}(*methods, &block) # def self.before_save(*methods, &block)
+ callbacks = CallbackChain.build(:#{callback}, *methods, &block) # callbacks = CallbackChain.build(:before_save, *methods, &block)
+ @#{callback}_callbacks ||= CallbackChain.new # @before_save_callbacks ||= CallbackChain.new
+ @#{callback}_callbacks.concat callbacks # @before_save_callbacks.concat callbacks
+ end # end
+ #
+ def self.#{callback}_callback_chain # def self.before_save_callback_chain
+ @#{callback}_callbacks ||= CallbackChain.new # @before_save_callbacks ||= CallbackChain.new
+ #
+ if superclass.respond_to?(:#{callback}_callback_chain) # if superclass.respond_to?(:before_save_callback_chain)
+ CallbackChain.new( # CallbackChain.new(
+ superclass.#{callback}_callback_chain + # superclass.before_save_callback_chain +
+ @#{callback}_callbacks # @before_save_callbacks
+ ) # )
+ else # else
+ @#{callback}_callbacks # @before_save_callbacks
+ end # end
+ end # end
end_eval
end
end
diff --git a/activesupport/lib/active_support/core_ext/benchmark.rb b/activesupport/lib/active_support/core_ext/benchmark.rb
index 79ba165e3a..ae57b152e8 100644
--- a/activesupport/lib/active_support/core_ext/benchmark.rb
+++ b/activesupport/lib/active_support/core_ext/benchmark.rb
@@ -1,12 +1,19 @@
require 'benchmark'
class << Benchmark
- remove_method :realtime
+ # Earlier Ruby had a slower implementation.
+ if RUBY_VERSION < '1.8.7'
+ remove_method :realtime
- def realtime
- r0 = Time.now
- yield
- r1 = Time.now
- r1.to_f - r0.to_f
+ def realtime
+ r0 = Time.now
+ yield
+ r1 = Time.now
+ r1.to_f - r0.to_f
+ end
+ end
+
+ def ms
+ 1000 * realtime { yield }
end
end
diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
index 186ca69c05..c795871474 100644
--- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb
@@ -11,17 +11,17 @@ class Class
syms.flatten.each do |sym|
next if sym.is_a?(Hash)
class_eval(<<-EOS, __FILE__, __LINE__)
- unless defined? @@#{sym}
- @@#{sym} = nil
- end
-
- def self.#{sym}
- @@#{sym}
- end
-
- def #{sym}
- @@#{sym}
- end
+ unless defined? @@#{sym} # unless defined? @@hair_colors
+ @@#{sym} = nil # @@hair_colors = nil
+ end # end
+ #
+ def self.#{sym} # def self.hair_colors
+ @@#{sym} # @@hair_colors
+ end # end
+ #
+ def #{sym} # def hair_colors
+ @@#{sym} # @@hair_colors
+ end # end
EOS
end
end
@@ -30,19 +30,19 @@ class Class
options = syms.extract_options!
syms.flatten.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__)
- unless defined? @@#{sym}
- @@#{sym} = nil
- end
-
- def self.#{sym}=(obj)
- @@#{sym} = obj
- end
-
- #{"
- def #{sym}=(obj)
- @@#{sym} = obj
- end
- " unless options[:instance_writer] == false }
+ unless defined? @@#{sym} # unless defined? @@hair_colors
+ @@#{sym} = nil # @@hair_colors = nil
+ end # end
+ #
+ def self.#{sym}=(obj) # def self.hair_colors=(obj)
+ @@#{sym} = obj # @@hair_colors = obj
+ end # end
+ #
+ #{" #
+ def #{sym}=(obj) # def hair_colors=(obj)
+ @@#{sym} = obj # @@hair_colors = obj
+ end # end
+ " unless options[:instance_writer] == false } # # instance writer above is generated unless options[:instance_writer] == false
EOS
end
end
diff --git a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
index 368317df9b..000ccf4d55 100644
--- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
+++ b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
@@ -9,22 +9,23 @@ class Class
class_name_to_stop_searching_on = self.superclass.name.blank? ? "Object" : self.superclass.name
names.each do |name|
class_eval <<-EOS
- def self.#{name}
- if defined?(@#{name})
- @#{name}
- elsif superclass < #{class_name_to_stop_searching_on} && superclass.respond_to?(:#{name})
- superclass.#{name}
- end
- end
- def #{name}
- self.class.#{name}
- end
- def self.#{name}?
- !!#{name}
- end
- def #{name}?
- !!#{name}
- end
+ def self.#{name} # def self.only_reader
+ if defined?(@#{name}) # if defined?(@only_reader)
+ @#{name} # @only_reader
+ elsif superclass < #{class_name_to_stop_searching_on} && # elsif superclass < Object &&
+ superclass.respond_to?(:#{name}) # superclass.respond_to?(:only_reader)
+ superclass.#{name} # superclass.only_reader
+ end # end
+ end # end
+ def #{name} # def only_reader
+ self.class.#{name} # self.class.only_reader
+ end # end
+ def self.#{name}? # def self.only_reader?
+ !!#{name} # !!only_reader
+ end # end
+ def #{name}? # def only_reader?
+ !!#{name} # !!only_reader
+ end # end
EOS
end
end
@@ -32,9 +33,9 @@ class Class
def superclass_delegating_writer(*names)
names.each do |name|
class_eval <<-EOS
- def self.#{name}=(value)
- @#{name} = value
- end
+ def self.#{name}=(value) # def self.only_writer=(value)
+ @#{name} = value # @only_writer = value
+ end # end
EOS
end
end
diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
index e6143a274b..1794afe77c 100644
--- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
+++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
@@ -11,13 +11,13 @@ class Class # :nodoc:
syms.each do |sym|
next if sym.is_a?(Hash)
class_eval <<-EOS
- def self.#{sym}
- read_inheritable_attribute(:#{sym})
- end
-
- def #{sym}
- self.class.#{sym}
- end
+ def self.#{sym} # def self.before_add_for_comments
+ read_inheritable_attribute(:#{sym}) # read_inheritable_attribute(:before_add_for_comments)
+ end # end
+ #
+ def #{sym} # def before_add_for_comments
+ self.class.#{sym} # self.class.before_add_for_comments
+ end # end
EOS
end
end
@@ -26,15 +26,15 @@ class Class # :nodoc:
options = syms.extract_options!
syms.each do |sym|
class_eval <<-EOS
- def self.#{sym}=(obj)
- write_inheritable_attribute(:#{sym}, obj)
- end
-
- #{"
- def #{sym}=(obj)
- self.class.#{sym} = obj
- end
- " unless options[:instance_writer] == false }
+ def self.#{sym}=(obj) # def self.color=(obj)
+ write_inheritable_attribute(:#{sym}, obj) # write_inheritable_attribute(:color, obj)
+ end # end
+ #
+ #{" #
+ def #{sym}=(obj) # def color=(obj)
+ self.class.#{sym} = obj # self.class.color = obj
+ end # end
+ " unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
EOS
end
end
@@ -43,15 +43,15 @@ class Class # :nodoc:
options = syms.extract_options!
syms.each do |sym|
class_eval <<-EOS
- def self.#{sym}=(obj)
- write_inheritable_array(:#{sym}, obj)
- end
-
- #{"
- def #{sym}=(obj)
- self.class.#{sym} = obj
- end
- " unless options[:instance_writer] == false }
+ def self.#{sym}=(obj) # def self.levels=(obj)
+ write_inheritable_array(:#{sym}, obj) # write_inheritable_array(:levels, obj)
+ end # end
+ #
+ #{" #
+ def #{sym}=(obj) # def levels=(obj)
+ self.class.#{sym} = obj # self.class.levels = obj
+ end # end
+ " unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
EOS
end
end
@@ -60,15 +60,15 @@ class Class # :nodoc:
options = syms.extract_options!
syms.each do |sym|
class_eval <<-EOS
- def self.#{sym}=(obj)
- write_inheritable_hash(:#{sym}, obj)
- end
-
- #{"
- def #{sym}=(obj)
- self.class.#{sym} = obj
- end
- " unless options[:instance_writer] == false }
+ def self.#{sym}=(obj) # def self.nicknames=(obj)
+ write_inheritable_hash(:#{sym}, obj) # write_inheritable_hash(:nicknames, obj)
+ end # end
+ #
+ #{" #
+ def #{sym}=(obj) # def nicknames=(obj)
+ self.class.#{sym} = obj # self.class.nicknames = obj
+ end # end
+ " unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
EOS
end
end
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index 437b44c51c..a254e45624 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -94,8 +94,7 @@ module ActiveSupport #:nodoc:
options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]),
:root => "hash" })
options[:builder].instruct! unless options.delete(:skip_instruct)
- dasherize = !options.has_key?(:dasherize) || options[:dasherize]
- root = dasherize ? options[:root].to_s.dasherize : options[:root].to_s
+ root = rename_key(options[:root].to_s, options)
options[:builder].__send__(:method_missing, root) do
each do |key, value|
@@ -122,7 +121,7 @@ module ActiveSupport #:nodoc:
else
type_name = XML_TYPE_NAMES[value.class.name]
- key = dasherize ? key.to_s.dasherize : key.to_s
+ key = rename_key(key.to_s, options)
attributes = options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name }
if value.nil?
@@ -142,9 +141,16 @@ module ActiveSupport #:nodoc:
end
+ def rename_key(key, options = {})
+ camelize = options.has_key?(:camelize) && options[:camelize]
+ dasherize = !options.has_key?(:dasherize) || options[:dasherize]
+ key = key.camelize if camelize
+ dasherize ? key.dasherize : key
+ end
+
module ClassMethods
def from_xml(xml)
- typecast_xml_value(undasherize_keys(XmlMini.parse(xml)))
+ typecast_xml_value(unrename_keys(XmlMini.parse(xml)))
end
private
@@ -210,15 +216,15 @@ module ActiveSupport #:nodoc:
end
end
- def undasherize_keys(params)
+ def unrename_keys(params)
case params.class.to_s
when "Hash"
params.inject({}) do |h,(k,v)|
- h[k.to_s.tr("-", "_")] = undasherize_keys(v)
+ h[k.to_s.underscore.tr("-", "_")] = unrename_keys(v)
h
end
when "Array"
- params.map { |v| undasherize_keys(v) }
+ params.map { |v| unrename_keys(v) }
else
params
end
diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb
index 88df49a69f..d845a6d8ca 100644
--- a/activesupport/lib/active_support/core_ext/hash/slice.rb
+++ b/activesupport/lib/active_support/core_ext/hash/slice.rb
@@ -24,10 +24,17 @@ module ActiveSupport #:nodoc:
end
# Replaces the hash with only the given keys.
+ # Returns a hash contained the removed key/value pairs
+ # {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d =>4}
def slice!(*keys)
- replace(slice(*keys))
+ keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
+ omit = slice(*self.keys - keys)
+ hash = slice(*keys)
+ replace(hash)
+ omit
end
end
end
end
end
+
diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb
index 24fe7294c9..858da7aa07 100644
--- a/activesupport/lib/active_support/core_ext/logger.rb
+++ b/activesupport/lib/active_support/core_ext/logger.rb
@@ -3,12 +3,12 @@
class Logger
def self.define_around_helper(level)
module_eval <<-end_eval
- def around_#{level}(before_message, after_message, &block)
- self.#{level}(before_message)
- return_value = block.call(self)
- self.#{level}(after_message)
- return return_value
- end
+ def around_#{level}(before_message, after_message, &block) # def around_debug(before_message, after_message, &block)
+ self.#{level}(before_message) # self.debug(before_message)
+ return_value = block.call(self) # return_value = block.call(self)
+ self.#{level}(after_message) # self.debug(after_message)
+ return return_value # return return_value
+ end # end
end_eval
end
[:debug, :info, :error, :fatal].each {|level| define_around_helper(level) }
diff --git a/activesupport/lib/active_support/core_ext/module/aliasing.rb b/activesupport/lib/active_support/core_ext/module/aliasing.rb
index e640f64520..10fa520ba1 100644
--- a/activesupport/lib/active_support/core_ext/module/aliasing.rb
+++ b/activesupport/lib/active_support/core_ext/module/aliasing.rb
@@ -64,9 +64,9 @@ module ActiveSupport
# e.title # => "Megastars"
def alias_attribute(new_name, old_name)
module_eval <<-STR, __FILE__, __LINE__+1
- def #{new_name}; self.#{old_name}; end
- def #{new_name}?; self.#{old_name}?; end
- def #{new_name}=(v); self.#{old_name} = v; end
+ def #{new_name}; self.#{old_name}; end # def subject; self.title; end
+ def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
+ def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
STR
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb b/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
index 683789d853..4d0198f028 100644
--- a/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
+++ b/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
@@ -22,10 +22,10 @@ class Module
raise 'Default value or block required' unless !default.nil? || block
define_method(sym, block_given? ? block : Proc.new { default })
module_eval(<<-EVAL, __FILE__, __LINE__)
- def #{sym}=(value)
- class << self; attr_reader :#{sym} end
- @#{sym} = value
- end
+ def #{sym}=(value) # def age=(value)
+ class << self; attr_reader :#{sym} end # class << self; attr_reader :age end
+ @#{sym} = value # @age = value
+ end # end
EVAL
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
index 51e1c9af90..9402cb8534 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -15,17 +15,17 @@ class Module
syms.each do |sym|
next if sym.is_a?(Hash)
class_eval(<<-EOS, __FILE__, __LINE__)
- unless defined? @@#{sym}
- @@#{sym} = nil
- end
-
- def self.#{sym}
- @@#{sym}
- end
-
- def #{sym}
- @@#{sym}
- end
+ unless defined? @@#{sym} # unless defined? @@pagination_options
+ @@#{sym} = nil # @@pagination_options = nil
+ end # end
+ #
+ def self.#{sym} # def self.pagination_options
+ @@#{sym} # @@pagination_options
+ end # end
+ #
+ def #{sym} # def pagination_options
+ @@#{sym} # @@pagination_options
+ end # end
EOS
end
end
@@ -34,19 +34,19 @@ class Module
options = syms.extract_options!
syms.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__)
- unless defined? @@#{sym}
- @@#{sym} = nil
- end
-
- def self.#{sym}=(obj)
- @@#{sym} = obj
- end
-
- #{"
- def #{sym}=(obj)
- @@#{sym} = obj
- end
- " unless options[:instance_writer] == false }
+ unless defined? @@#{sym} # unless defined? @@pagination_options
+ @@#{sym} = nil # @@pagination_options = nil
+ end # end
+ #
+ def self.#{sym}=(obj) # def self.pagination_options=(obj)
+ @@#{sym} = obj # @@pagination_options = obj
+ end # end
+ #
+ #{" #
+ def #{sym}=(obj) # def pagination_options=(obj)
+ @@#{sym} = obj # @@pagination_options = obj
+ end # end
+ " unless options[:instance_writer] == false } # # instance writer above is generated unless options[:instance_writer] == false
EOS
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 2905eebc85..fb4b5f0f3c 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -72,6 +72,30 @@ class Module
# invoice.customer_name # => "John Doe"
# invoice.customer_address # => "Vimmersvej 13"
#
+ # If the object to which you delegate can be nil, you may want to use the
+ # :allow_nil option. In that case, it returns nil instead of raising a
+ # NoMethodError exception:
+ #
+ # class Foo
+ # attr_accessor :bar
+ # def initialize(bar = nil)
+ # @bar = bar
+ # end
+ # delegate :zoo, :to => :bar
+ # end
+ #
+ # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
+ #
+ # class Foo
+ # attr_accessor :bar
+ # def initialize(bar = nil)
+ # @bar = bar
+ # end
+ # delegate :zoo, :to => :bar, :allow_nil => true
+ # end
+ #
+ # Foo.new.zoo # returns nil
+ #
def delegate(*methods)
options = methods.pop
unless options.is_a?(Hash) && to = options[:to]
@@ -84,11 +108,13 @@ class Module
prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"
+ allow_nil = options[:allow_nil] && "#{to} && "
+
methods.each do |method|
module_eval(<<-EOS, "(__DELEGATION__)", 1)
- def #{prefix}#{method}(*args, &block)
- #{to}.__send__(#{method.inspect}, *args, &block)
- end
+ def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
+ #{allow_nil}#{to}.__send__(#{method.inspect}, *args, &block) # client && client.__send__(:name, *args, &block)
+ end # end
EOS
end
end
diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb
index 251606024e..069db3fed0 100644
--- a/activesupport/lib/active_support/core_ext/module/synchronization.rb
+++ b/activesupport/lib/active_support/core_ext/module/synchronization.rb
@@ -26,11 +26,11 @@ class Module
end
module_eval(<<-EOS, __FILE__, __LINE__)
- def #{aliased_method}_with_synchronization#{punctuation}(*args, &block)
- #{with}.synchronize do
- #{aliased_method}_without_synchronization#{punctuation}(*args, &block)
- end
- end
+ def #{aliased_method}_with_synchronization#{punctuation}(*args, &block) # def expire_with_synchronization(*args, &block)
+ #{with}.synchronize do # @@lock.synchronize do
+ #{aliased_method}_without_synchronization#{punctuation}(*args, &block) # expire_without_synchronization(*args, &block)
+ end # end
+ end # end
EOS
alias_method_chain method, :synchronization
diff --git a/activesupport/lib/active_support/core_ext/object/misc.rb b/activesupport/lib/active_support/core_ext/object/misc.rb
index 46f9c7d676..4570570bbc 100644
--- a/activesupport/lib/active_support/core_ext/object/misc.rb
+++ b/activesupport/lib/active_support/core_ext/object/misc.rb
@@ -40,6 +40,21 @@ class Object
value
end
+ # Yields <code>x</code> to the block, and then returns <code>x</code>.
+ # The primary purpose of this method is to "tap into" a method chain,
+ # in order to perform operations on intermediate results within the chain.
+ #
+ # (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
+ # tap { |x| puts "array: #{x.inspect}" }.
+ # select { |x| x%2 == 0 }.
+ # tap { |x| puts "evens: #{x.inspect}" }.
+ # map { |x| x*x }.
+ # tap { |x| puts "squares: #{x.inspect}" }
+ def tap
+ yield self
+ self
+ end unless Object.respond_to?(:tap)
+
# An elegant way to factor duplication out of options passed to a series of
# method calls. Each method called in the block, with the block variable as
# the receiver, will have its options merged with the default +options+ hash
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index 293450c180..7ce9adec2c 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -559,9 +559,9 @@ module ActiveSupport #:nodoc:
# Old style environment.rb referenced this method directly. Please note, it doesn't
# actually *do* anything any more.
def self.root(*args)
- if defined?(RAILS_DEFAULT_LOGGER)
- RAILS_DEFAULT_LOGGER.warn "Your environment.rb uses the old syntax, it may not continue to work in future releases."
- RAILS_DEFAULT_LOGGER.warn "For upgrade instructions please see: http://manuals.rubyonrails.com/read/book/19"
+ if defined?(Rails) && Rails.logger
+ Rails.logger.warn "Your environment.rb uses the old syntax, it may not continue to work in future releases."
+ Rails.logger.warn "For upgrade instructions please see: http://manuals.rubyonrails.com/read/book/19"
end
end
end
diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb
index 543e3b08d2..d20151661b 100644
--- a/activesupport/lib/active_support/deprecation.rb
+++ b/activesupport/lib/active_support/deprecation.rb
@@ -13,7 +13,7 @@ module ActiveSupport
$stderr.puts callstack.join("\n ") if debug
},
'development' => Proc.new { |message, callstack|
- logger = defined?(::RAILS_DEFAULT_LOGGER) ? ::RAILS_DEFAULT_LOGGER : Logger.new($stderr)
+ logger = defined?(Rails) ? Rails.logger : Logger.new($stderr)
logger.warn message
logger.debug callstack.join("\n ") if debug
}
@@ -90,10 +90,15 @@ module ActiveSupport
method_names.each do |method_name|
alias_method_chain(method_name, :deprecation) do |target, punctuation|
class_eval(<<-EOS, __FILE__, __LINE__)
- def #{target}_with_deprecation#{punctuation}(*args, &block)
- ::ActiveSupport::Deprecation.warn(self.class.deprecated_method_warning(:#{method_name}, #{options[method_name].inspect}), caller)
- #{target}_without_deprecation#{punctuation}(*args, &block)
- end
+ def #{target}_with_deprecation#{punctuation}(*args, &block) # def generate_secret_with_deprecation(*args, &block)
+ ::ActiveSupport::Deprecation.warn( # ::ActiveSupport::Deprecation.warn(
+ self.class.deprecated_method_warning( # self.class.deprecated_method_warning(
+ :#{method_name}, # :generate_secret,
+ #{options[method_name].inspect}), # "You should use ActiveSupport::SecureRandom.hex(64)"),
+ caller # caller
+ ) # )
+ #{target}_without_deprecation#{punctuation}(*args, &block) # generate_secret_without_deprecation(*args, &block)
+ end # end
EOS
end
end
diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb
index fdb219dbf7..9da4048272 100644
--- a/activesupport/lib/active_support/json/decoding.rb
+++ b/activesupport/lib/active_support/json/decoding.rb
@@ -16,7 +16,7 @@ module ActiveSupport
protected
# matches YAML-formatted dates
- DATE_REGEX = /^\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?$/
+ DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?)$/
# Ensure that ":" and "," are always followed by a space
def convert_json_to_yaml(json) #:nodoc:
diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb
index 9f2fd3a401..952b4d8063 100644
--- a/activesupport/lib/active_support/memoizable.rb
+++ b/activesupport/lib/active_support/memoizable.rb
@@ -59,34 +59,36 @@ module ActiveSupport
memoized_ivar = ActiveSupport::Memoizable.memoized_ivar_for(symbol)
class_eval <<-EOS, __FILE__, __LINE__
- include InstanceMethods
-
- raise "Already memoized #{symbol}" if method_defined?(:#{original_method})
- alias #{original_method} #{symbol}
-
- if instance_method(:#{symbol}).arity == 0
- def #{symbol}(reload = false)
- if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty?
- #{memoized_ivar} = [#{original_method}.freeze]
- end
- #{memoized_ivar}[0]
- end
- else
- def #{symbol}(*args)
- #{memoized_ivar} ||= {} unless frozen?
- reload = args.pop if args.last == true || args.last == :reload
-
- if defined?(#{memoized_ivar}) && #{memoized_ivar}
- if !reload && #{memoized_ivar}.has_key?(args)
- #{memoized_ivar}[args]
- elsif #{memoized_ivar}
- #{memoized_ivar}[args] = #{original_method}(*args).freeze
- end
- else
- #{original_method}(*args)
- end
- end
- end
+ include InstanceMethods # include InstanceMethods
+ #
+ if method_defined?(:#{original_method}) # if method_defined?(:_unmemoized_mime_type)
+ raise "Already memoized #{symbol}" # raise "Already memoized mime_type"
+ end # end
+ alias #{original_method} #{symbol} # alias _unmemoized_mime_type mime_type
+ #
+ if instance_method(:#{symbol}).arity == 0 # if instance_method(:mime_type).arity == 0
+ def #{symbol}(reload = false) # def mime_type(reload = false)
+ if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty? # if reload || !defined?(@_memoized_mime_type) || @_memoized_mime_type.empty?
+ #{memoized_ivar} = [#{original_method}.freeze] # @_memoized_mime_type = [_unmemoized_mime_type.freeze]
+ end # end
+ #{memoized_ivar}[0] # @_memoized_mime_type[0]
+ end # end
+ else # else
+ def #{symbol}(*args) # def mime_type(*args)
+ #{memoized_ivar} ||= {} unless frozen? # @_memoized_mime_type ||= {} unless frozen?
+ reload = args.pop if args.last == true || args.last == :reload # reload = args.pop if args.last == true || args.last == :reload
+ #
+ if defined?(#{memoized_ivar}) && #{memoized_ivar} # if defined?(@_memoized_mime_type) && @_memoized_mime_type
+ if !reload && #{memoized_ivar}.has_key?(args) # if !reload && @_memoized_mime_type.has_key?(args)
+ #{memoized_ivar}[args] # @_memoized_mime_type[args]
+ elsif #{memoized_ivar} # elsif @_memoized_mime_type
+ #{memoized_ivar}[args] = #{original_method}(*args).freeze # @_memoized_mime_type[args] = _unmemoized_mime_type(*args).freeze
+ end # end
+ else # else
+ #{original_method}(*args) # _unmemoized_mime_type(*args)
+ end # end
+ end # end
+ end # end
EOS
end
end
diff --git a/activesupport/lib/active_support/multibyte/unicode_database.rb b/activesupport/lib/active_support/multibyte/unicode_database.rb
index 3b8cf8f9eb..a08f38cdbb 100644
--- a/activesupport/lib/active_support/multibyte/unicode_database.rb
+++ b/activesupport/lib/active_support/multibyte/unicode_database.rb
@@ -24,10 +24,10 @@ module ActiveSupport #:nodoc:
# Lazy load the Unicode database so it's only loaded when it's actually used
ATTRIBUTES.each do |attr_name|
class_eval(<<-EOS, __FILE__, __LINE__)
- def #{attr_name}
- load
- @#{attr_name}
- end
+ def #{attr_name} # def codepoints
+ load # load
+ @#{attr_name} # @codepoints
+ end # end
EOS
end
diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb
index 5de94c67e0..3def0be639 100644
--- a/activesupport/lib/active_support/ordered_hash.rb
+++ b/activesupport/lib/active_support/ordered_hash.rb
@@ -4,62 +4,85 @@ module ActiveSupport
if RUBY_VERSION >= '1.9'
OrderedHash = ::Hash
else
- class OrderedHash < Array #:nodoc:
+ class OrderedHash < Hash #:nodoc:
+ def initialize(*args, &block)
+ super
+ @keys = []
+ end
+
def []=(key, value)
- if pair = assoc(key)
- pair.pop
- pair << value
- else
- self << [key, value]
+ if !has_key?(key)
+ @keys << key
end
- value
+ super
end
- def [](key)
- pair = assoc(key)
- pair ? pair.last : nil
+ def delete(key)
+ if has_key? key
+ index = @keys.index(key)
+ @keys.delete_at index
+ end
+ super
end
- def delete(key)
- pair = assoc(key)
- pair ? array_index = index(pair) : nil
- array_index ? delete_at(array_index).last : nil
+ def reject!
+ super
+ sync_keys!
+ self
+ end
+
+ def reject(&block)
+ dup.reject!(&block)
end
def keys
- collect { |key, value| key }
+ @keys
end
def values
- collect { |key, value| value }
+ @keys.collect { |key| self[key] }
end
def to_hash
- returning({}) do |hash|
- each { |array| hash[array[0]] = array[1] }
- end
+ Hash.new(self)
+ end
+
+ def each_key
+ @keys.each { |key| yield key }
+ end
+
+ def each_value
+ @keys.each { |key| yield self[key]}
end
- def has_key?(k)
- !assoc(k).nil?
+ def each
+ keys.each {|key| yield [key, self[key]]}
end
- alias_method :key?, :has_key?
- alias_method :include?, :has_key?
- alias_method :member?, :has_key?
+ alias_method :each_pair, :each
- def has_value?(v)
- any? { |key, value| value == v }
+ def clear
+ super
+ @keys.clear
+ self
end
- alias_method :value?, :has_value?
+ def shift
+ k = @keys.first
+ v = delete(k)
+ [k, v]
+ end
- def each_key
- each { |key, value| yield key }
+ def merge(other_hash)
+ result = dup
+ other_hash.each {|k,v| result[k]=v}
+ result
end
- def each_value
- each { |key, value| yield value }
+ private
+
+ def sync_keys!
+ @keys.delete_if {|k| !has_key?(k)}
end
end
end
diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb
index bd136c2596..f8d12e82b3 100644
--- a/activesupport/lib/active_support/testing/performance.rb
+++ b/activesupport/lib/active_support/testing/performance.rb
@@ -12,7 +12,7 @@ module ActiveSupport
if benchmark = ARGV.include?('--benchmark') # HAX for rake test
{ :benchmark => true,
:runs => 4,
- :metrics => [:process_time, :memory, :objects, :gc_runs, :gc_time],
+ :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time],
:output => 'tmp/performance' }
else
{ :benchmark => false,
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 9a2d283b30..99be89fdf0 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -199,7 +199,7 @@ module ActiveSupport
# If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
# otherwise move backwards #utc, for accuracy when moving across DST boundaries
if other.acts_like?(:time)
- utc - other
+ utc.to_f - other.to_f
elsif duration_of_variable_length?(other)
method_missing(:-, other)
else
@@ -234,9 +234,9 @@ module ActiveSupport
%w(year mon month day mday wday yday hour min sec to_date).each do |method_name|
class_eval <<-EOV
- def #{method_name}
- time.#{method_name}
- end
+ def #{method_name} # def year
+ time.#{method_name} # time.year
+ end # end
EOV
end
diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb
index 463610722c..3d7d52ca71 100644
--- a/activesupport/lib/active_support/vendor.rb
+++ b/activesupport/lib/active_support/vendor.rb
@@ -9,9 +9,9 @@ end
require 'builder'
begin
- gem 'memcache-client', '~> 1.5.1'
+ gem 'memcache-client', '~> 1.5.0.5'
rescue Gem::LoadError
- $:.unshift "#{File.dirname(__FILE__)}/vendor/memcache-client-1.5.1"
+ $:.unshift "#{File.dirname(__FILE__)}/vendor/memcache-client-1.5.0.5"
end
begin
@@ -22,8 +22,8 @@ end
# TODO I18n gem has not been released yet
# begin
-# gem 'i18n', '~> 0.0.1'
+# gem 'i18n', '~> 0.1.1'
# rescue Gem::LoadError
- $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.0.1"
+ $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.1.1/lib"
require 'i18n'
# end
diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/.gitignore b/activesupport/lib/active_support/vendor/i18n-0.1.1/.gitignore
new file mode 100644
index 0000000000..0f41a39f89
--- /dev/null
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/.gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+test/rails/fixtures
+doc
diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/MIT-LICENSE b/activesupport/lib/active_support/vendor/i18n-0.1.1/MIT-LICENSE
new file mode 100755
index 0000000000..ed8e9ee66d
--- /dev/null
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008 The Ruby I18n team
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/README.textile b/activesupport/lib/active_support/vendor/i18n-0.1.1/README.textile
new file mode 100644
index 0000000000..a07fc8426d
--- /dev/null
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/README.textile
@@ -0,0 +1,20 @@
+h1. Ruby I18n gem
+
+I18n and localization solution for Ruby.
+
+For information please refer to http://rails-i18n.org
+
+h2. Authors
+
+* "Matt Aimonetti":http://railsontherun.com
+* "Sven Fuchs":http://www.artweb-design.de
+* "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey
+* "Saimon Moore":http://saimonmoore.net
+* "Stephan Soller":http://www.arkanis-development.de
+
+h2. License
+
+MIT License. See the included MIT-LICENCE file.
+
+
+
diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/Rakefile b/activesupport/lib/active_support/vendor/i18n-0.1.1/Rakefile
new file mode 100644
index 0000000000..2164e13e69
--- /dev/null
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/Rakefile
@@ -0,0 +1,5 @@
+task :default => [:test]
+
+task :test do
+ ruby "test/all.rb"
+end
diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/i18n.gemspec b/activesupport/lib/active_support/vendor/i18n-0.1.1/i18n.gemspec
new file mode 100644
index 0000000000..14294606bd
--- /dev/null
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/i18n.gemspec
@@ -0,0 +1,27 @@
+Gem::Specification.new do |s|
+ s.name = "i18n"
+ s.version = "0.1.1"
+ s.date = "2008-10-26"
+ s.summary = "Internationalization support for Ruby"
+ s.email = "rails-i18n@googlegroups.com"
+ s.homepage = "http://rails-i18n.org"
+ s.description = "Add Internationalization support to your Ruby application."
+ s.has_rdoc = false
+ s.authors = ['Sven Fuchs', 'Joshua Harvey', 'Matt Aimonetti', 'Stephan Soller', 'Saimon Moore']
+ s.files = [
+ 'i18n.gemspec',
+ 'lib/i18n/backend/simple.rb',
+ 'lib/i18n/exceptions.rb',
+ 'lib/i18n.rb',
+ 'MIT-LICENSE',
+ 'README.textile'
+ ]
+ s.test_files = [
+ 'test/all.rb',
+ 'test/i18n_exceptions_test.rb',
+ 'test/i18n_test.rb',
+ 'test/locale/en.rb',
+ 'test/locale/en.yml',
+ 'test/simple_backend_test.rb'
+ ]
+end
diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb b/activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n.rb
index 2ffe3618b5..b5ad094d0e 100755
--- a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.rb
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n.rb
@@ -2,39 +2,39 @@
# Sven Fuchs (http://www.artweb-design.de),
# Joshua Harvey (http://www.workingwithrails.com/person/759-joshua-harvey),
# Saimon Moore (http://saimonmoore.net),
-# Stephan Soller (http://www.arkanis-development.de/)
+# Stephan Soller (http://www.arkanis-development.de/)
# Copyright:: Copyright (c) 2008 The Ruby i18n Team
# License:: MIT
require 'i18n/backend/simple'
require 'i18n/exceptions'
-module I18n
+module I18n
@@backend = nil
@@load_path = nil
@@default_locale = :'en'
@@exception_handler = :default_exception_handler
-
+
class << self
# Returns the current backend. Defaults to +Backend::Simple+.
def backend
@@backend ||= Backend::Simple.new
end
-
+
# Sets the current backend. Used to set a custom backend.
- def backend=(backend)
+ def backend=(backend)
@@backend = backend
end
-
- # Returns the current default locale. Defaults to 'en'
+
+ # Returns the current default locale. Defaults to :'en'
def default_locale
- @@default_locale
+ @@default_locale
end
-
+
# Sets the current default locale. Used to set a custom default locale.
- def default_locale=(locale)
- @@default_locale = locale
+ def default_locale=(locale)
+ @@default_locale = locale
end
-
+
# Returns the current locale. Defaults to I18n.default_locale.
def locale
Thread.current[:locale] ||= default_locale
@@ -44,12 +44,12 @@ module I18n
def locale=(locale)
Thread.current[:locale] = locale
end
-
+
# Sets the exception handler.
def exception_handler=(exception_handler)
@@exception_handler = exception_handler
end
-
+
# Allow clients to register paths providing translation data sources. The
# backend defines acceptable sources.
#
@@ -74,25 +74,25 @@ module I18n
def reload!
backend.reload!
end
-
- # Translates, pluralizes and interpolates a given key using a given locale,
+
+ # Translates, pluralizes and interpolates a given key using a given locale,
# scope, and default, as well as interpolation values.
#
# *LOOKUP*
#
- # Translation data is organized as a nested hash using the upper-level keys
- # as namespaces. <em>E.g.</em>, ActionView ships with the translation:
+ # Translation data is organized as a nested hash using the upper-level keys
+ # as namespaces. <em>E.g.</em>, ActionView ships with the translation:
# <tt>:date => {:formats => {:short => "%b %d"}}</tt>.
- #
- # Translations can be looked up at any level of this hash using the key argument
- # and the scope option. <em>E.g.</em>, in this example <tt>I18n.t :date</tt>
+ #
+ # Translations can be looked up at any level of this hash using the key argument
+ # and the scope option. <em>E.g.</em>, in this example <tt>I18n.t :date</tt>
# returns the whole translations hash <tt>{:formats => {:short => "%b %d"}}</tt>.
- #
- # Key can be either a single key or a dot-separated key (both Strings and Symbols
+ #
+ # Key can be either a single key or a dot-separated key (both Strings and Symbols
# work). <em>E.g.</em>, the short format can be looked up using both:
# I18n.t 'date.formats.short'
# I18n.t :'date.formats.short'
- #
+ #
# Scope can be either a single key, a dot-separated key or an array of keys
# or dot-separated keys. Keys and scopes can be combined freely. So these
# examples will all look up the same short date format:
@@ -105,9 +105,9 @@ module I18n
#
# Translations can contain interpolation variables which will be replaced by
# values passed to #translate as part of the options hash, with the keys matching
- # the interpolation variable names.
+ # the interpolation variable names.
#
- # <em>E.g.</em>, with a translation <tt>:foo => "foo {{bar}}"</tt> the option
+ # <em>E.g.</em>, with a translation <tt>:foo => "foo {{bar}}"</tt> the option
# value for the key +bar+ will be interpolated into the translation:
# I18n.t :foo, :bar => 'baz' # => 'foo baz'
#
@@ -116,7 +116,7 @@ module I18n
# Translation data can contain pluralized translations. Pluralized translations
# are arrays of singluar/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
#
- # Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English
+ # Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English
# pluralization rules. Other algorithms can be supported by custom backends.
#
# This returns the singular version of a pluralized translation:
@@ -125,9 +125,9 @@ module I18n
# These both return the plural version of a pluralized translation:
# I18n.t :foo, :count => 0 # => 'Foos'
# I18n.t :foo, :count => 2 # => 'Foos'
- #
- # The <tt>:count</tt> option can be used both for pluralization and interpolation.
- # <em>E.g.</em>, with the translation
+ #
+ # The <tt>:count</tt> option can be used both for pluralization and interpolation.
+ # <em>E.g.</em>, with the translation
# <tt>:foo => ['{{count}} foo', '{{count}} foos']</tt>, count will
# be interpolated to the pluralized translation:
# I18n.t :foo, :count => 1 # => '1 foo'
@@ -137,11 +137,11 @@ module I18n
# This returns the translation for <tt>:foo</tt> or <tt>default</tt> if no translation was found:
# I18n.t :foo, :default => 'default'
#
- # This returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> if no
+ # This returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> if no
# translation for <tt>:foo</tt> was found:
# I18n.t :foo, :default => :bar
#
- # Returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt>
+ # Returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt>
# or <tt>default</tt> if no translations for <tt>:foo</tt> and <tt>:bar</tt> were found.
# I18n.t :foo, :default => [:bar, 'default']
#
@@ -161,9 +161,9 @@ module I18n
rescue I18n::ArgumentError => e
raise e if options[:raise]
send(@@exception_handler, e, locale, key, options)
- end
+ end
alias :t :translate
-
+
# Localizes certain objects, such as dates and numbers to local formatting.
def localize(object, options = {})
locale = options[:locale] || I18n.locale
@@ -171,7 +171,7 @@ module I18n
backend.localize(locale, object, format)
end
alias :l :localize
-
+
protected
# Handles exceptions raised in the backend. All exceptions except for
# MissingTranslationData exceptions are re-raised. When a MissingTranslationData
@@ -181,7 +181,7 @@ module I18n
return exception.message if MissingTranslationData === exception
raise exception
end
-
+
# Merges the given locale, key and scope into a single array of keys.
# Splits keys that contain dots into multiple keys. Makes sure all
# keys are Symbols.
@@ -191,4 +191,4 @@ module I18n
keys.flatten.map { |k| k.to_sym }
end
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n/backend/simple.rb
index bdda55d3fe..d298b3a85a 100644
--- a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n/backend/simple.rb
@@ -6,21 +6,21 @@ module I18n
INTERPOLATION_RESERVED_KEYS = %w(scope default)
MATCH = /(\\\\)?\{\{([^\}]+)\}\}/
- # Accepts a list of paths to translation files. Loads translations from
+ # Accepts a list of paths to translation files. Loads translations from
# plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
# for details.
def load_translations(*filenames)
filenames.each { |filename| load_file(filename) }
end
-
- # Stores translations for the given locale in memory.
+
+ # Stores translations for the given locale in memory.
# This uses a deep merge for the translations hash, so existing
# translations will be overwritten by new ones only at the deepest
# level of the hash.
def store_translations(locale, data)
merge_translations(locale, data)
end
-
+
def translate(locale, key, options = {})
raise InvalidLocale.new(locale) if locale.nil?
return key.map { |k| translate(locale, k, options) } if key.is_a? Array
@@ -41,13 +41,13 @@ module I18n
entry = interpolate(locale, entry, values)
entry
end
-
- # Acts the same as +strftime+, but returns a localized version of the
- # formatted date string. Takes a key from the date/time formats
- # translations as a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
+
+ # Acts the same as +strftime+, but returns a localized version of the
+ # formatted date string. Takes a key from the date/time formats
+ # translations as a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
def localize(locale, object, format = :default)
raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
-
+
type = object.respond_to?(:sec) ? 'time' : 'date'
# TODO only translate these if format is a String?
formats = translate(locale, :"#{type}.formats")
@@ -57,14 +57,14 @@ module I18n
# TODO only translate these if the format string is actually present
# TODO check which format strings are present, then bulk translate then, then replace them
- format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday])
+ format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday])
format.gsub!(/%A/, translate(locale, :"date.day_names")[object.wday])
format.gsub!(/%b/, translate(locale, :"date.abbr_month_names")[object.mon])
format.gsub!(/%B/, translate(locale, :"date.month_names")[object.mon])
format.gsub!(/%p/, translate(locale, :"time.#{object.hour < 12 ? :am : :pm}")) if object.respond_to? :hour
object.strftime(format)
end
-
+
def initialized?
@initialized ||= false
end
@@ -79,12 +79,12 @@ module I18n
load_translations(*I18n.load_path)
@initialized = true
end
-
+
def translations
@translations ||= {}
end
-
- # Looks up a translation from the translations hash. Returns nil if
+
+ # Looks up a translation from the translations hash. Returns nil if
# eiher key is nil, or locale, scope or key do not exist as a key in the
# nested translations hash. Splits keys or scopes containing dots
# into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
@@ -101,19 +101,19 @@ module I18n
end
end
end
-
- # Evaluates a default translation.
+
+ # Evaluates a default translation.
# If the given default is a String it is used literally. If it is a Symbol
# it will be translated with the given options. If it is an Array the first
# translation yielded will be returned.
- #
- # <em>I.e.</em>, <tt>default(locale, [:foo, 'default'])</tt> will return +default+ if
+ #
+ # <em>I.e.</em>, <tt>default(locale, [:foo, 'default'])</tt> will return +default+ if
# <tt>translate(locale, :foo)</tt> does not yield a result.
def default(locale, default, options = {})
case default
when String then default
when Symbol then translate locale, default, options
- when Array then default.each do |obj|
+ when Array then default.each do |obj|
result = default(locale, obj, options.dup) and return result
end and nil
end
@@ -135,10 +135,10 @@ module I18n
end
# Interpolates values into a given string.
- #
- # interpolate "file {{file}} opened by \\{{user}}", :file => 'test.txt', :user => 'Mr. X'
+ #
+ # interpolate "file {{file}} opened by \\{{user}}", :file => 'test.txt', :user => 'Mr. X'
# # => "file test.txt opened by {{user}}"
- #
+ #
# Note that you have to double escape the <tt>\\</tt> when you want to escape
# the <tt>{{...}}</tt> key in a string (once for the string and once for the
# interpolation).
@@ -167,8 +167,8 @@ module I18n
result.force_encoding(original_encoding) if original_encoding
result
end
-
- # Loads a single translations file by delegating to #load_rb or
+
+ # Loads a single translations file by delegating to #load_rb or
# #load_yml depending on the file extension and directly merges the
# data to the existing translations. Raises I18n::UnknownFileType
# for all other file extensions.
@@ -178,19 +178,19 @@ module I18n
data = send :"load_#{type}", filename # TODO raise a meaningful exception if this does not yield a Hash
data.each { |locale, d| merge_translations(locale, d) }
end
-
+
# Loads a plain Ruby translations file. eval'ing the file must yield
# a Hash containing translation data with locales as toplevel keys.
def load_rb(filename)
eval(IO.read(filename), binding, filename)
end
-
- # Loads a YAML translations file. The data must have locales as
+
+ # Loads a YAML translations file. The data must have locales as
# toplevel keys.
def load_yml(filename)
YAML::load(IO.read(filename))
end
-
+
# Deep merges the given translations hash with the existing translations
# for the given locale
def merge_translations(locale, data)
@@ -202,7 +202,7 @@ module I18n
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
translations[locale].merge!(data, &merger)
end
-
+
# Return a new hash with all keys and nested keys converted to symbols.
def deep_symbolize_keys(hash)
hash.inject({}) { |result, (key, value)|
diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/exceptions.rb b/activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n/exceptions.rb
index 0f3eff1071..b5cea7acb4 100644
--- a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n/exceptions.rb
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/lib/i18n/exceptions.rb
@@ -1,6 +1,6 @@
module I18n
class ArgumentError < ::ArgumentError; end
-
+
class InvalidLocale < ArgumentError
attr_reader :locale
def initialize(locale)
@@ -42,7 +42,7 @@ module I18n
super "reserved key #{key.inspect} used in #{string.inspect}"
end
end
-
+
class UnknownFileType < ArgumentError
attr_reader :type, :filename
def initialize(type, filename)
@@ -50,4 +50,4 @@ module I18n
super "can not load translations from #{filename}, the file type #{type} is not known"
end
end
-end \ No newline at end of file
+end \ No newline at end of file
diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/all.rb b/activesupport/lib/active_support/vendor/i18n-0.1.1/test/all.rb
new file mode 100644
index 0000000000..353712da49
--- /dev/null
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/test/all.rb
@@ -0,0 +1,5 @@
+dir = File.dirname(__FILE__)
+require dir + '/i18n_test.rb'
+require dir + '/simple_backend_test.rb'
+require dir + '/i18n_exceptions_test.rb'
+# *require* dir + '/custom_backend_test.rb' \ No newline at end of file
diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/i18n_exceptions_test.rb b/activesupport/lib/active_support/vendor/i18n-0.1.1/test/i18n_exceptions_test.rb
new file mode 100644
index 0000000000..dfcba6901f
--- /dev/null
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/test/i18n_exceptions_test.rb
@@ -0,0 +1,100 @@
+$:.unshift "lib"
+
+require 'rubygems'
+require 'test/unit'
+require 'mocha'
+require 'i18n'
+require 'active_support'
+
+class I18nExceptionsTest < Test::Unit::TestCase
+ def test_invalid_locale_stores_locale
+ force_invalid_locale
+ rescue I18n::ArgumentError => e
+ assert_nil e.locale
+ end
+
+ def test_invalid_locale_message
+ force_invalid_locale
+ rescue I18n::ArgumentError => e
+ assert_equal 'nil is not a valid locale', e.message
+ end
+
+ def test_missing_translation_data_stores_locale_key_and_options
+ force_missing_translation_data
+ rescue I18n::ArgumentError => e
+ options = {:scope => :bar}
+ assert_equal 'de', e.locale
+ assert_equal :foo, e.key
+ assert_equal options, e.options
+ end
+
+ def test_missing_translation_data_message
+ force_missing_translation_data
+ rescue I18n::ArgumentError => e
+ assert_equal 'translation missing: de, bar, foo', e.message
+ end
+
+ def test_invalid_pluralization_data_stores_entry_and_count
+ force_invalid_pluralization_data
+ rescue I18n::ArgumentError => e
+ assert_equal [:bar], e.entry
+ assert_equal 1, e.count
+ end
+
+ def test_invalid_pluralization_data_message
+ force_invalid_pluralization_data
+ rescue I18n::ArgumentError => e
+ assert_equal 'translation data [:bar] can not be used with :count => 1', e.message
+ end
+
+ def test_missing_interpolation_argument_stores_key_and_string
+ force_missing_interpolation_argument
+ rescue I18n::ArgumentError => e
+ assert_equal 'bar', e.key
+ assert_equal "{{bar}}", e.string
+ end
+
+ def test_missing_interpolation_argument_message
+ force_missing_interpolation_argument
+ rescue I18n::ArgumentError => e
+ assert_equal 'interpolation argument bar missing in "{{bar}}"', e.message
+ end
+
+ def test_reserved_interpolation_key_stores_key_and_string
+ force_reserved_interpolation_key
+ rescue I18n::ArgumentError => e
+ assert_equal 'scope', e.key
+ assert_equal "{{scope}}", e.string
+ end
+
+ def test_reserved_interpolation_key_message
+ force_reserved_interpolation_key
+ rescue I18n::ArgumentError => e
+ assert_equal 'reserved key "scope" used in "{{scope}}"', e.message
+ end
+
+ private
+ def force_invalid_locale
+ I18n.backend.translate nil, :foo
+ end
+
+ def force_missing_translation_data
+ I18n.backend.store_translations 'de', :bar => nil
+ I18n.backend.translate 'de', :foo, :scope => :bar
+ end
+
+ def force_invalid_pluralization_data
+ I18n.backend.store_translations 'de', :foo => [:bar]
+ I18n.backend.translate 'de', :foo, :count => 1
+ end
+
+ def force_missing_interpolation_argument
+ I18n.backend.store_translations 'de', :foo => "{{bar}}"
+ I18n.backend.translate 'de', :foo, :baz => 'baz'
+ end
+
+ def force_reserved_interpolation_key
+ I18n.backend.store_translations 'de', :foo => "{{scope}}"
+ I18n.backend.translate 'de', :foo, :baz => 'baz'
+ end
+end \ No newline at end of file
diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/i18n_test.rb b/activesupport/lib/active_support/vendor/i18n-0.1.1/test/i18n_test.rb
new file mode 100644
index 0000000000..bbb35ec809
--- /dev/null
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/test/i18n_test.rb
@@ -0,0 +1,125 @@
+$:.unshift "lib"
+
+require 'rubygems'
+require 'test/unit'
+require 'mocha'
+require 'i18n'
+require 'active_support'
+
+class I18nTest < Test::Unit::TestCase
+ def setup
+ I18n.backend.store_translations :'en', {
+ :currency => {
+ :format => {
+ :separator => '.',
+ :delimiter => ',',
+ }
+ }
+ }
+ end
+
+ def test_uses_simple_backend_set_by_default
+ assert I18n.backend.is_a?(I18n::Backend::Simple)
+ end
+
+ def test_can_set_backend
+ assert_nothing_raised{ I18n.backend = self }
+ assert_equal self, I18n.backend
+ I18n.backend = I18n::Backend::Simple.new
+ end
+
+ def test_uses_en_us_as_default_locale_by_default
+ assert_equal 'en', I18n.default_locale
+ end
+
+ def test_can_set_default_locale
+ assert_nothing_raised{ I18n.default_locale = 'de' }
+ assert_equal 'de', I18n.default_locale
+ I18n.default_locale = 'en'
+ end
+
+ def test_uses_default_locale_as_locale_by_default
+ assert_equal I18n.default_locale, I18n.locale
+ end
+
+ def test_can_set_locale_to_thread_current
+ assert_nothing_raised{ I18n.locale = 'de' }
+ assert_equal 'de', I18n.locale
+ assert_equal 'de', Thread.current[:locale]
+ I18n.locale = 'en'
+ end
+
+ def test_can_set_exception_handler
+ assert_nothing_raised{ I18n.exception_handler = :custom_exception_handler }
+ I18n.exception_handler = :default_exception_handler # revert it
+ end
+
+ def test_uses_custom_exception_handler
+ I18n.exception_handler = :custom_exception_handler
+ I18n.expects(:custom_exception_handler)
+ I18n.translate :bogus
+ I18n.exception_handler = :default_exception_handler # revert it
+ end
+
+ def test_delegates_translate_to_backend
+ I18n.backend.expects(:translate).with 'de', :foo, {}
+ I18n.translate :foo, :locale => 'de'
+ end
+
+ def test_delegates_localize_to_backend
+ I18n.backend.expects(:localize).with 'de', :whatever, :default
+ I18n.localize :whatever, :locale => 'de'
+ end
+
+ def test_translate_given_no_locale_uses_i18n_locale
+ I18n.backend.expects(:translate).with 'en', :foo, {}
+ I18n.translate :foo
+ end
+
+ def test_translate_on_nested_symbol_keys_works
+ assert_equal ".", I18n.t(:'currency.format.separator')
+ end
+
+ def test_translate_with_nested_string_keys_works
+ assert_equal ".", I18n.t('currency.format.separator')
+ end
+
+ def test_translate_with_array_as_scope_works
+ assert_equal ".", I18n.t(:separator, :scope => ['currency.format'])
+ end
+
+ def test_translate_with_array_containing_dot_separated_strings_as_scope_works
+ assert_equal ".", I18n.t(:separator, :scope => ['currency.format'])
+ end
+
+ def test_translate_with_key_array_and_dot_separated_scope_works
+ assert_equal [".", ","], I18n.t(%w(separator delimiter), :scope => 'currency.format')
+ end
+
+ def test_translate_with_dot_separated_key_array_and_scope_works
+ assert_equal [".", ","], I18n.t(%w(format.separator format.delimiter), :scope => 'currency')
+ end
+
+ def test_translate_with_options_using_scope_works
+ I18n.backend.expects(:translate).with('de', :precision, :scope => :"currency.format")
+ I18n.with_options :locale => 'de', :scope => :'currency.format' do |locale|
+ locale.t :precision
+ end
+ end
+
+ # def test_translate_given_no_args_raises_missing_translation_data
+ # assert_equal "translation missing: en, no key", I18n.t
+ # end
+
+ def test_translate_given_a_bogus_key_raises_missing_translation_data
+ assert_equal "translation missing: en, bogus", I18n.t(:bogus)
+ end
+
+ def test_localize_nil_raises_argument_error
+ assert_raises(I18n::ArgumentError) { I18n.l nil }
+ end
+
+ def test_localize_object_raises_argument_error
+ assert_raises(I18n::ArgumentError) { I18n.l Object.new }
+ end
+end
diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/locale/en.rb b/activesupport/lib/active_support/vendor/i18n-0.1.1/test/locale/en.rb
new file mode 100644
index 0000000000..6044ce10d9
--- /dev/null
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/test/locale/en.rb
@@ -0,0 +1 @@
+{:'en-Ruby' => {:foo => {:bar => "baz"}}} \ No newline at end of file
diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/locale/en.yml b/activesupport/lib/active_support/vendor/i18n-0.1.1/test/locale/en.yml
new file mode 100644
index 0000000000..0b298c9c0e
--- /dev/null
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/test/locale/en.yml
@@ -0,0 +1,3 @@
+en-Yaml:
+ foo:
+ bar: baz \ No newline at end of file
diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.1/test/simple_backend_test.rb b/activesupport/lib/active_support/vendor/i18n-0.1.1/test/simple_backend_test.rb
new file mode 100644
index 0000000000..e181975f38
--- /dev/null
+++ b/activesupport/lib/active_support/vendor/i18n-0.1.1/test/simple_backend_test.rb
@@ -0,0 +1,502 @@
+# encoding: utf-8
+$:.unshift "lib"
+
+require 'rubygems'
+require 'test/unit'
+require 'mocha'
+require 'i18n'
+require 'time'
+require 'yaml'
+
+module I18nSimpleBackendTestSetup
+ def setup_backend
+ # backend_reset_translations!
+ @backend = I18n::Backend::Simple.new
+ @backend.store_translations 'en', :foo => {:bar => 'bar', :baz => 'baz'}
+ @locale_dir = File.dirname(__FILE__) + '/locale'
+ end
+ alias :setup :setup_backend
+
+ # def backend_reset_translations!
+ # I18n::Backend::Simple::ClassMethods.send :class_variable_set, :@@translations, {}
+ # end
+
+ def backend_get_translations
+ # I18n::Backend::Simple::ClassMethods.send :class_variable_get, :@@translations
+ @backend.instance_variable_get :@translations
+ end
+
+ def add_datetime_translations
+ @backend.store_translations :'de', {
+ :date => {
+ :formats => {
+ :default => "%d.%m.%Y",
+ :short => "%d. %b",
+ :long => "%d. %B %Y",
+ },
+ :day_names => %w(Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag),
+ :abbr_day_names => %w(So Mo Di Mi Do Fr Sa),
+ :month_names => %w(Januar Februar März April Mai Juni Juli August September Oktober November Dezember).unshift(nil),
+ :abbr_month_names => %w(Jan Feb Mar Apr Mai Jun Jul Aug Sep Okt Nov Dez).unshift(nil),
+ :order => [:day, :month, :year]
+ },
+ :time => {
+ :formats => {
+ :default => "%a, %d. %b %Y %H:%M:%S %z",
+ :short => "%d. %b %H:%M",
+ :long => "%d. %B %Y %H:%M",
+ },
+ :am => 'am',
+ :pm => 'pm'
+ },
+ :datetime => {
+ :distance_in_words => {
+ :half_a_minute => 'half a minute',
+ :less_than_x_seconds => {
+ :one => 'less than 1 second',
+ :other => 'less than {{count}} seconds'
+ },
+ :x_seconds => {
+ :one => '1 second',
+ :other => '{{count}} seconds'
+ },
+ :less_than_x_minutes => {
+ :one => 'less than a minute',
+ :other => 'less than {{count}} minutes'
+ },
+ :x_minutes => {
+ :one => '1 minute',
+ :other => '{{count}} minutes'
+ },
+ :about_x_hours => {
+ :one => 'about 1 hour',
+ :other => 'about {{count}} hours'
+ },
+ :x_days => {
+ :one => '1 day',
+ :other => '{{count}} days'
+ },
+ :about_x_months => {
+ :one => 'about 1 month',
+ :other => 'about {{count}} months'
+ },
+ :x_months => {
+ :one => '1 month',
+ :other => '{{count}} months'
+ },
+ :about_x_years => {
+ :one => 'about 1 year',
+ :other => 'about {{count}} year'
+ },
+ :over_x_years => {
+ :one => 'over 1 year',
+ :other => 'over {{count}} years'
+ }
+ }
+ }
+ }
+ end
+end
+
+class I18nSimpleBackendTranslationsTest < Test::Unit::TestCase
+ include I18nSimpleBackendTestSetup
+
+ def test_store_translations_adds_translations # no, really :-)
+ @backend.store_translations :'en', :foo => 'bar'
+ assert_equal Hash[:'en', {:foo => 'bar'}], backend_get_translations
+ end
+
+ def test_store_translations_deep_merges_translations
+ @backend.store_translations :'en', :foo => {:bar => 'bar'}
+ @backend.store_translations :'en', :foo => {:baz => 'baz'}
+ assert_equal Hash[:'en', {:foo => {:bar => 'bar', :baz => 'baz'}}], backend_get_translations
+ end
+
+ def test_store_translations_forces_locale_to_sym
+ @backend.store_translations 'en', :foo => 'bar'
+ assert_equal Hash[:'en', {:foo => 'bar'}], backend_get_translations
+ end
+
+ def test_store_translations_converts_keys_to_symbols
+ # backend_reset_translations!
+ @backend.store_translations 'en', 'foo' => {'bar' => 'bar', 'baz' => 'baz'}
+ assert_equal Hash[:'en', {:foo => {:bar => 'bar', :baz => 'baz'}}], backend_get_translations
+ end
+end
+
+class I18nSimpleBackendTranslateTest < Test::Unit::TestCase
+ include I18nSimpleBackendTestSetup
+
+ def test_translate_calls_lookup_with_locale_given
+ @backend.expects(:lookup).with('de', :bar, [:foo]).returns 'bar'
+ @backend.translate 'de', :bar, :scope => [:foo]
+ end
+
+ def test_given_no_keys_it_returns_the_default
+ assert_equal 'default', @backend.translate('en', nil, :default => 'default')
+ end
+
+ def test_translate_given_a_symbol_as_a_default_translates_the_symbol
+ assert_equal 'bar', @backend.translate('en', nil, :scope => [:foo], :default => :bar)
+ end
+
+ def test_translate_given_an_array_as_default_uses_the_first_match
+ assert_equal 'bar', @backend.translate('en', :does_not_exist, :scope => [:foo], :default => [:does_not_exist_2, :bar])
+ end
+
+ def test_translate_given_an_array_of_inexistent_keys_it_raises_missing_translation_data
+ assert_raises I18n::MissingTranslationData do
+ @backend.translate('en', :does_not_exist, :scope => [:foo], :default => [:does_not_exist_2, :does_not_exist_3])
+ end
+ end
+
+ def test_translate_an_array_of_keys_translates_all_of_them
+ assert_equal %w(bar baz), @backend.translate('en', [:bar, :baz], :scope => [:foo])
+ end
+
+ def test_translate_calls_pluralize
+ @backend.expects(:pluralize).with 'en', 'bar', 1
+ @backend.translate 'en', :bar, :scope => [:foo], :count => 1
+ end
+
+ def test_translate_calls_interpolate
+ @backend.expects(:interpolate).with 'en', 'bar', {}
+ @backend.translate 'en', :bar, :scope => [:foo]
+ end
+
+ def test_translate_calls_interpolate_including_count_as_a_value
+ @backend.expects(:interpolate).with 'en', 'bar', {:count => 1}
+ @backend.translate 'en', :bar, :scope => [:foo], :count => 1
+ end
+
+ def test_translate_given_nil_as_a_locale_raises_an_argument_error
+ assert_raises(I18n::InvalidLocale){ @backend.translate nil, :bar }
+ end
+
+ def test_translate_with_a_bogus_key_and_no_default_raises_missing_translation_data
+ assert_raises(I18n::MissingTranslationData){ @backend.translate 'de', :bogus }
+ end
+end
+
+class I18nSimpleBackendLookupTest < Test::Unit::TestCase
+ include I18nSimpleBackendTestSetup
+
+ # useful because this way we can use the backend with no key for interpolation/pluralization
+ def test_lookup_given_nil_as_a_key_returns_nil
+ assert_nil @backend.send(:lookup, 'en', nil)
+ end
+
+ def test_lookup_given_nested_keys_looks_up_a_nested_hash_value
+ assert_equal 'bar', @backend.send(:lookup, 'en', :bar, [:foo])
+ end
+end
+
+class I18nSimpleBackendPluralizeTest < Test::Unit::TestCase
+ include I18nSimpleBackendTestSetup
+
+ def test_pluralize_given_nil_returns_the_given_entry
+ entry = {:one => 'bar', :other => 'bars'}
+ assert_equal entry, @backend.send(:pluralize, nil, entry, nil)
+ end
+
+ def test_pluralize_given_0_returns_zero_string_if_zero_key_given
+ assert_equal 'zero', @backend.send(:pluralize, nil, {:zero => 'zero', :one => 'bar', :other => 'bars'}, 0)
+ end
+
+ def test_pluralize_given_0_returns_plural_string_if_no_zero_key_given
+ assert_equal 'bars', @backend.send(:pluralize, nil, {:one => 'bar', :other => 'bars'}, 0)
+ end
+
+ def test_pluralize_given_1_returns_singular_string
+ assert_equal 'bar', @backend.send(:pluralize, nil, {:one => 'bar', :other => 'bars'}, 1)
+ end
+
+ def test_pluralize_given_2_returns_plural_string
+ assert_equal 'bars', @backend.send(:pluralize, nil, {:one => 'bar', :other => 'bars'}, 2)
+ end
+
+ def test_pluralize_given_3_returns_plural_string
+ assert_equal 'bars', @backend.send(:pluralize, nil, {:one => 'bar', :other => 'bars'}, 3)
+ end
+
+ def test_interpolate_given_incomplete_pluralization_data_raises_invalid_pluralization_data
+ assert_raises(I18n::InvalidPluralizationData){ @backend.send(:pluralize, nil, {:one => 'bar'}, 2) }
+ end
+
+ # def test_interpolate_given_a_string_raises_invalid_pluralization_data
+ # assert_raises(I18n::InvalidPluralizationData){ @backend.send(:pluralize, nil, 'bar', 2) }
+ # end
+ #
+ # def test_interpolate_given_an_array_raises_invalid_pluralization_data
+ # assert_raises(I18n::InvalidPluralizationData){ @backend.send(:pluralize, nil, ['bar'], 2) }
+ # end
+end
+
+class I18nSimpleBackendInterpolateTest < Test::Unit::TestCase
+ include I18nSimpleBackendTestSetup
+
+ def test_interpolate_given_a_value_hash_interpolates_the_values_to_the_string
+ assert_equal 'Hi David!', @backend.send(:interpolate, nil, 'Hi {{name}}!', :name => 'David')
+ end
+
+ def test_interpolate_given_a_value_hash_interpolates_into_unicode_string
+ assert_equal 'Häi David!', @backend.send(:interpolate, nil, 'Häi {{name}}!', :name => 'David')
+ end
+
+ def test_interpolate_given_nil_as_a_string_returns_nil
+ assert_nil @backend.send(:interpolate, nil, nil, :name => 'David')
+ end
+
+ def test_interpolate_given_an_non_string_as_a_string_returns_nil
+ assert_equal [], @backend.send(:interpolate, nil, [], :name => 'David')
+ end
+
+ def test_interpolate_given_a_values_hash_with_nil_values_interpolates_the_string
+ assert_equal 'Hi !', @backend.send(:interpolate, nil, 'Hi {{name}}!', {:name => nil})
+ end
+
+ def test_interpolate_given_an_empty_values_hash_raises_missing_interpolation_argument
+ assert_raises(I18n::MissingInterpolationArgument) { @backend.send(:interpolate, nil, 'Hi {{name}}!', {}) }
+ end
+
+ def test_interpolate_given_a_string_containing_a_reserved_key_raises_reserved_interpolation_key
+ assert_raises(I18n::ReservedInterpolationKey) { @backend.send(:interpolate, nil, '{{default}}', {:default => nil}) }
+ end
+end
+
+class I18nSimpleBackendLocalizeDateTest < Test::Unit::TestCase
+ include I18nSimpleBackendTestSetup
+
+ def setup
+ @backend = I18n::Backend::Simple.new
+ add_datetime_translations
+ @date = Date.new 2008, 1, 1
+ end
+
+ def test_translate_given_the_short_format_it_uses_it
+ assert_equal '01. Jan', @backend.localize('de', @date, :short)
+ end
+
+ def test_translate_given_the_long_format_it_uses_it
+ assert_equal '01. Januar 2008', @backend.localize('de', @date, :long)
+ end
+
+ def test_translate_given_the_default_format_it_uses_it
+ assert_equal '01.01.2008', @backend.localize('de', @date, :default)
+ end
+
+ def test_translate_given_a_day_name_format_it_returns_a_day_name
+ assert_equal 'Dienstag', @backend.localize('de', @date, '%A')
+ end
+
+ def test_translate_given_an_abbr_day_name_format_it_returns_an_abbrevated_day_name
+ assert_equal 'Di', @backend.localize('de', @date, '%a')
+ end
+
+ def test_translate_given_a_month_name_format_it_returns_a_month_name
+ assert_equal 'Januar', @backend.localize('de', @date, '%B')
+ end
+
+ def test_translate_given_an_abbr_month_name_format_it_returns_an_abbrevated_month_name
+ assert_equal 'Jan', @backend.localize('de', @date, '%b')
+ end
+
+ def test_translate_given_no_format_it_does_not_fail
+ assert_nothing_raised{ @backend.localize 'de', @date }
+ end
+
+ def test_translate_given_an_unknown_format_it_does_not_fail
+ assert_nothing_raised{ @backend.localize 'de', @date, '%x' }
+ end
+
+ def test_localize_nil_raises_argument_error
+ assert_raises(I18n::ArgumentError) { @backend.localize 'de', nil }
+ end
+
+ def test_localize_object_raises_argument_error
+ assert_raises(I18n::ArgumentError) { @backend.localize 'de', Object.new }
+ end
+end
+
+class I18nSimpleBackendLocalizeDateTimeTest < Test::Unit::TestCase
+ include I18nSimpleBackendTestSetup
+
+ def setup
+ @backend = I18n::Backend::Simple.new
+ add_datetime_translations
+ @morning = DateTime.new 2008, 1, 1, 6
+ @evening = DateTime.new 2008, 1, 1, 18
+ end
+
+ def test_translate_given_the_short_format_it_uses_it
+ assert_equal '01. Jan 06:00', @backend.localize('de', @morning, :short)
+ end
+
+ def test_translate_given_the_long_format_it_uses_it
+ assert_equal '01. Januar 2008 06:00', @backend.localize('de', @morning, :long)
+ end
+
+ def test_translate_given_the_default_format_it_uses_it
+ assert_equal 'Di, 01. Jan 2008 06:00:00 +0000', @backend.localize('de', @morning, :default)
+ end
+
+ def test_translate_given_a_day_name_format_it_returns_the_correct_day_name
+ assert_equal 'Dienstag', @backend.localize('de', @morning, '%A')
+ end
+
+ def test_translate_given_an_abbr_day_name_format_it_returns_the_correct_abbrevated_day_name
+ assert_equal 'Di', @backend.localize('de', @morning, '%a')
+ end
+
+ def test_translate_given_a_month_name_format_it_returns_the_correct_month_name
+ assert_equal 'Januar', @backend.localize('de', @morning, '%B')
+ end
+
+ def test_translate_given_an_abbr_month_name_format_it_returns_the_correct_abbrevated_month_name
+ assert_equal 'Jan', @backend.localize('de', @morning, '%b')
+ end
+
+ def test_translate_given_a_meridian_indicator_format_it_returns_the_correct_meridian_indicator
+ assert_equal 'am', @backend.localize('de', @morning, '%p')
+ assert_equal 'pm', @backend.localize('de', @evening, '%p')
+ end
+
+ def test_translate_given_no_format_it_does_not_fail
+ assert_nothing_raised{ @backend.localize 'de', @morning }
+ end
+
+ def test_translate_given_an_unknown_format_it_does_not_fail
+ assert_nothing_raised{ @backend.localize 'de', @morning, '%x' }
+ end
+end
+
+class I18nSimpleBackendLocalizeTimeTest < Test::Unit::TestCase
+ include I18nSimpleBackendTestSetup
+
+ def setup
+ @old_timezone, ENV['TZ'] = ENV['TZ'], 'UTC'
+ @backend = I18n::Backend::Simple.new
+ add_datetime_translations
+ @morning = Time.parse '2008-01-01 6:00 UTC'
+ @evening = Time.parse '2008-01-01 18:00 UTC'
+ end
+
+ def teardown
+ @old_timezone ? ENV['TZ'] = @old_timezone : ENV.delete('TZ')
+ end
+
+ def test_translate_given_the_short_format_it_uses_it
+ assert_equal '01. Jan 06:00', @backend.localize('de', @morning, :short)
+ end
+
+ def test_translate_given_the_long_format_it_uses_it
+ assert_equal '01. Januar 2008 06:00', @backend.localize('de', @morning, :long)
+ end
+
+ # TODO Seems to break on Windows because ENV['TZ'] is ignored. What's a better way to do this?
+ # def test_translate_given_the_default_format_it_uses_it
+ # assert_equal 'Di, 01. Jan 2008 06:00:00 +0000', @backend.localize('de', @morning, :default)
+ # end
+
+ def test_translate_given_a_day_name_format_it_returns_the_correct_day_name
+ assert_equal 'Dienstag', @backend.localize('de', @morning, '%A')
+ end
+
+ def test_translate_given_an_abbr_day_name_format_it_returns_the_correct_abbrevated_day_name
+ assert_equal 'Di', @backend.localize('de', @morning, '%a')
+ end
+
+ def test_translate_given_a_month_name_format_it_returns_the_correct_month_name
+ assert_equal 'Januar', @backend.localize('de', @morning, '%B')
+ end
+
+ def test_translate_given_an_abbr_month_name_format_it_returns_the_correct_abbrevated_month_name
+ assert_equal 'Jan', @backend.localize('de', @morning, '%b')
+ end
+
+ def test_translate_given_a_meridian_indicator_format_it_returns_the_correct_meridian_indicator
+ assert_equal 'am', @backend.localize('de', @morning, '%p')
+ assert_equal 'pm', @backend.localize('de', @evening, '%p')
+ end
+
+ def test_translate_given_no_format_it_does_not_fail
+ assert_nothing_raised{ @backend.localize 'de', @morning }
+ end
+
+ def test_translate_given_an_unknown_format_it_does_not_fail
+ assert_nothing_raised{ @backend.localize 'de', @morning, '%x' }
+ end
+end
+
+class I18nSimpleBackendHelperMethodsTest < Test::Unit::TestCase
+ def setup
+ @backend = I18n::Backend::Simple.new
+ end
+
+ def test_deep_symbolize_keys_works
+ result = @backend.send :deep_symbolize_keys, 'foo' => {'bar' => {'baz' => 'bar'}}
+ expected = {:foo => {:bar => {:baz => 'bar'}}}
+ assert_equal expected, result
+ end
+end
+
+class I18nSimpleBackendLoadTranslationsTest < Test::Unit::TestCase
+ include I18nSimpleBackendTestSetup
+
+ def test_load_translations_with_unknown_file_type_raises_exception
+ assert_raises(I18n::UnknownFileType) { @backend.load_translations "#{@locale_dir}/en.xml" }
+ end
+
+ def test_load_translations_with_ruby_file_type_does_not_raise_exception
+ assert_nothing_raised { @backend.load_translations "#{@locale_dir}/en.rb" }
+ end
+
+ def test_load_rb_loads_data_from_ruby_file
+ data = @backend.send :load_rb, "#{@locale_dir}/en.rb"
+ assert_equal({:'en-Ruby' => {:foo => {:bar => "baz"}}}, data)
+ end
+
+ def test_load_rb_loads_data_from_yaml_file
+ data = @backend.send :load_yml, "#{@locale_dir}/en.yml"
+ assert_equal({'en-Yaml' => {'foo' => {'bar' => 'baz'}}}, data)
+ end
+
+ def test_load_translations_loads_from_different_file_formats
+ @backend = I18n::Backend::Simple.new
+ @backend.load_translations "#{@locale_dir}/en.rb", "#{@locale_dir}/en.yml"
+ expected = {
+ :'en-Ruby' => {:foo => {:bar => "baz"}},
+ :'en-Yaml' => {:foo => {:bar => "baz"}}
+ }
+ assert_equal expected, backend_get_translations
+ end
+end
+
+class I18nSimpleBackendReloadTranslationsTest < Test::Unit::TestCase
+ include I18nSimpleBackendTestSetup
+
+ def setup
+ @backend = I18n::Backend::Simple.new
+ I18n.load_path = [File.dirname(__FILE__) + '/locale/en.yml']
+ assert_nil backend_get_translations
+ @backend.send :init_translations
+ end
+
+ def teardown
+ I18n.load_path = []
+ end
+
+ def test_setup
+ assert_not_nil backend_get_translations
+ end
+
+ def test_reload_translations_unloads_translations
+ @backend.reload!
+ assert_nil backend_get_translations
+ end
+
+ def test_reload_translations_uninitializes_translations
+ @backend.reload!
+ assert_equal @backend.initialized?, false
+ end
+end \ No newline at end of file
diff --git a/activesupport/lib/active_support/vendor/memcache-client-1.5.1/memcache.rb b/activesupport/lib/active_support/vendor/memcache-client-1.5.0.5/memcache.rb
index 99c9af0398..e90ddf3359 100644
--- a/activesupport/lib/active_support/vendor/memcache-client-1.5.1/memcache.rb
+++ b/activesupport/lib/active_support/vendor/memcache-client-1.5.0.5/memcache.rb
@@ -26,36 +26,13 @@
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+$TESTING = defined?($TESTING) && $TESTING
require 'socket'
require 'thread'
require 'timeout'
require 'rubygems'
-
-class String
-
- ##
- # Uses the ITU-T polynomial in the CRC32 algorithm.
-
- def crc32_ITU_T
- n = length
- r = 0xFFFFFFFF
-
- n.times do |i|
- r ^= self[i]
- 8.times do
- if (r & 1) != 0 then
- r = (r>>1) ^ 0xEDB88320
- else
- r >>= 1
- end
- end
- end
-
- r ^ 0xFFFFFFFF
- end
-
-end
+require 'zlib'
##
# A Ruby client library for memcached.
@@ -69,7 +46,7 @@ class MemCache
##
# The version of MemCache you are using.
- VERSION = '1.5.0'
+ VERSION = '1.5.0.5'
##
# Default options for the cache object.
@@ -78,6 +55,7 @@ class MemCache
:namespace => nil,
:readonly => false,
:multithread => false,
+ :failover => true
}
##
@@ -113,6 +91,10 @@ class MemCache
attr_reader :servers
##
+ # Whether this client should failover reads and writes to another server
+
+ attr_accessor :failover
+ ##
# Accepts a list of +servers+ and a list of +opts+. +servers+ may be
# omitted. See +servers=+ for acceptable server list arguments.
#
@@ -148,6 +130,7 @@ class MemCache
@namespace = opts[:namespace]
@readonly = opts[:readonly]
@multithread = opts[:multithread]
+ @failover = opts[:failover]
@mutex = Mutex.new if @multithread
@buckets = []
self.servers = servers
@@ -182,7 +165,7 @@ class MemCache
def servers=(servers)
# Create the server objects.
- @servers = servers.collect do |server|
+ @servers = Array(servers).collect do |server|
case server
when String
host, port, weight = server.split ':', 3
@@ -212,15 +195,12 @@ class MemCache
# 0. +key+ can not be decremented below 0.
def decr(key, amount = 1)
- server, cache_key = request_setup key
-
- if @multithread then
- threadsafe_cache_decr server, cache_key, amount
- else
+ raise MemCacheError, "Update of readonly cache" if @readonly
+ with_server(key) do |server, cache_key|
cache_decr server, cache_key, amount
end
- rescue TypeError, SocketError, SystemCallError, IOError => err
- handle_error server, err
+ rescue TypeError => err
+ handle_error nil, err
end
##
@@ -228,21 +208,14 @@ class MemCache
# unmarshalled.
def get(key, raw = false)
- server, cache_key = request_setup key
-
- value = if @multithread then
- threadsafe_cache_get server, cache_key
- else
- cache_get server, cache_key
- end
-
- return nil if value.nil?
-
- value = Marshal.load value unless raw
-
- return value
- rescue TypeError, SocketError, SystemCallError, IOError => err
- handle_error server, err
+ with_server(key) do |server, cache_key|
+ value = cache_get server, cache_key
+ return nil if value.nil?
+ value = Marshal.load value unless raw
+ return value
+ end
+ rescue TypeError => err
+ handle_error nil, err
end
##
@@ -280,36 +253,29 @@ class MemCache
server_keys.each do |server, keys_for_server|
keys_for_server = keys_for_server.join ' '
- values = if @multithread then
- threadsafe_cache_get_multi server, keys_for_server
- else
- cache_get_multi server, keys_for_server
- end
+ values = cache_get_multi server, keys_for_server
values.each do |key, value|
results[cache_keys[key]] = Marshal.load value
end
end
return results
- rescue TypeError, SocketError, SystemCallError, IOError => err
- handle_error server, err
+ rescue TypeError, IndexError => err
+ handle_error nil, err
end
##
- # Increments the value for +key+ by +amount+ and retruns the new value.
+ # Increments the value for +key+ by +amount+ and returns the new value.
# +key+ must already exist. If +key+ is not an integer, it is assumed to be
# 0.
def incr(key, amount = 1)
- server, cache_key = request_setup key
-
- if @multithread then
- threadsafe_cache_incr server, cache_key, amount
- else
+ raise MemCacheError, "Update of readonly cache" if @readonly
+ with_server(key) do |server, cache_key|
cache_incr server, cache_key, amount
end
- rescue TypeError, SocketError, SystemCallError, IOError => err
- handle_error server, err
+ rescue TypeError => err
+ handle_error nil, err
end
##
@@ -321,23 +287,23 @@ class MemCache
def set(key, value, expiry = 0, raw = false)
raise MemCacheError, "Update of readonly cache" if @readonly
- server, cache_key = request_setup key
- socket = server.socket
+ with_server(key) do |server, cache_key|
- value = Marshal.dump value unless raw
- command = "set #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"
+ value = Marshal.dump value unless raw
+ command = "set #{cache_key} 0 #{expiry} #{value.to_s.size}\r\n#{value}\r\n"
- begin
- @mutex.lock if @multithread
- socket.write command
- result = socket.gets
- raise_on_error_response! result
- result
- rescue SocketError, SystemCallError, IOError => err
- server.close
- raise MemCacheError, err.message
- ensure
- @mutex.unlock if @multithread
+ with_socket_management(server) do |socket|
+ socket.write command
+ result = socket.gets
+ raise_on_error_response! result
+
+ if result.nil?
+ server.close
+ raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
+ end
+
+ result
+ end
end
end
@@ -351,23 +317,16 @@ class MemCache
def add(key, value, expiry = 0, raw = false)
raise MemCacheError, "Update of readonly cache" if @readonly
- server, cache_key = request_setup key
- socket = server.socket
-
- value = Marshal.dump value unless raw
- command = "add #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"
-
- begin
- @mutex.lock if @multithread
- socket.write command
- result = socket.gets
- raise_on_error_response! result
- result
- rescue SocketError, SystemCallError, IOError => err
- server.close
- raise MemCacheError, err.message
- ensure
- @mutex.unlock if @multithread
+ with_server(key) do |server, cache_key|
+ value = Marshal.dump value unless raw
+ command = "add #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"
+
+ with_socket_management(server) do |socket|
+ socket.write command
+ result = socket.gets
+ raise_on_error_response! result
+ result
+ end
end
end
@@ -375,26 +334,15 @@ class MemCache
# Removes +key+ from the cache in +expiry+ seconds.
def delete(key, expiry = 0)
- @mutex.lock if @multithread
-
- raise MemCacheError, "No active servers" unless active?
- cache_key = make_cache_key key
- server = get_server_for_key cache_key
-
- sock = server.socket
- raise MemCacheError, "No connection to server" if sock.nil?
-
- begin
- sock.write "delete #{cache_key} #{expiry}\r\n"
- result = sock.gets
- raise_on_error_response! result
- result
- rescue SocketError, SystemCallError, IOError => err
- server.close
- raise MemCacheError, err.message
+ raise MemCacheError, "Update of readonly cache" if @readonly
+ with_server(key) do |server, cache_key|
+ with_socket_management(server) do |socket|
+ socket.write "delete #{cache_key} #{expiry}\r\n"
+ result = socket.gets
+ raise_on_error_response! result
+ result
+ end
end
- ensure
- @mutex.unlock if @multithread
end
##
@@ -403,21 +351,19 @@ class MemCache
def flush_all
raise MemCacheError, 'No active servers' unless active?
raise MemCacheError, "Update of readonly cache" if @readonly
+
begin
@mutex.lock if @multithread
@servers.each do |server|
- begin
- sock = server.socket
- raise MemCacheError, "No connection to server" if sock.nil?
- sock.write "flush_all\r\n"
- result = sock.gets
+ with_socket_management(server) do |socket|
+ socket.write "flush_all\r\n"
+ result = socket.gets
raise_on_error_response! result
result
- rescue SocketError, SystemCallError, IOError => err
- server.close
- raise MemCacheError, err.message
end
end
+ rescue IndexError => err
+ handle_error nil, err
ensure
@mutex.unlock if @multithread
end
@@ -469,14 +415,13 @@ class MemCache
server_stats = {}
@servers.each do |server|
- sock = server.socket
- raise MemCacheError, "No connection to server" if sock.nil?
+ next unless server.alive?
- value = nil
- begin
- sock.write "stats\r\n"
+ with_socket_management(server) do |socket|
+ value = nil
+ socket.write "stats\r\n"
stats = {}
- while line = sock.gets do
+ while line = socket.gets do
raise_on_error_response! line
break if line == "END\r\n"
if line =~ /\ASTAT ([\w]+) ([\w\.\:]+)/ then
@@ -498,12 +443,10 @@ class MemCache
end
end
server_stats["#{server.host}:#{server.port}"] = stats
- rescue SocketError, SystemCallError, IOError => err
- server.close
- raise MemCacheError, err.message
end
end
+ raise MemCacheError, "No active servers" if server_stats.empty?
server_stats
end
@@ -520,7 +463,7 @@ class MemCache
set key, value
end
- protected
+ protected unless $TESTING
##
# Create a key for the cache, incorporating the namespace qualifier if
@@ -537,7 +480,7 @@ class MemCache
##
# Pick a server to handle the request based on a hash of the key.
- def get_server_for_key(key)
+ def get_server_for_key(key, options = {})
raise ArgumentError, "illegal character in key #{key.inspect}" if
key =~ /\s/
raise ArgumentError, "key too long #{key.inspect}" if key.length > 250
@@ -545,13 +488,17 @@ class MemCache
return @servers.first if @servers.length == 1
hkey = hash_for key
-
- 20.times do |try|
- server = @buckets[hkey % @buckets.nitems]
- return server if server.alive?
- hkey += hash_for "#{try}#{key}"
+
+ if @failover
+ 20.times do |try|
+ server = @buckets[hkey % @buckets.compact.size]
+ return server if server.alive?
+ hkey += hash_for "#{try}#{key}"
+ end
+ else
+ return @buckets[hkey % @buckets.compact.size]
end
-
+
raise MemCacheError, "No servers available"
end
@@ -560,7 +507,7 @@ class MemCache
# sketchy for down servers).
def hash_for(key)
- (key.crc32_ITU_T >> 16) & 0x7fff
+ (Zlib.crc32(key) >> 16) & 0x7fff
end
##
@@ -568,12 +515,13 @@ class MemCache
# found.
def cache_decr(server, cache_key, amount)
- socket = server.socket
- socket.write "decr #{cache_key} #{amount}\r\n"
- text = socket.gets
- raise_on_error_response! text
- return nil if text == "NOT_FOUND\r\n"
- return text.to_i
+ with_socket_management(server) do |socket|
+ socket.write "decr #{cache_key} #{amount}\r\n"
+ text = socket.gets
+ raise_on_error_response! text
+ return nil if text == "NOT_FOUND\r\n"
+ return text.to_i
+ end
end
##
@@ -581,52 +529,54 @@ class MemCache
# miss.
def cache_get(server, cache_key)
- socket = server.socket
- socket.write "get #{cache_key}\r\n"
- keyline = socket.gets # "VALUE <key> <flags> <bytes>\r\n"
+ with_socket_management(server) do |socket|
+ socket.write "get #{cache_key}\r\n"
+ keyline = socket.gets # "VALUE <key> <flags> <bytes>\r\n"
- if keyline.nil? then
- server.close
- raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
- end
+ if keyline.nil? then
+ server.close
+ raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
+ end
- raise_on_error_response! keyline
- return nil if keyline == "END\r\n"
+ raise_on_error_response! keyline
+ return nil if keyline == "END\r\n"
- unless keyline =~ /(\d+)\r/ then
- server.close
- raise MemCacheError, "unexpected response #{keyline.inspect}"
+ unless keyline =~ /(\d+)\r/ then
+ server.close
+ raise MemCacheError, "unexpected response #{keyline.inspect}"
+ end
+ value = socket.read $1.to_i
+ socket.read 2 # "\r\n"
+ socket.gets # "END\r\n"
+ return value
end
- value = socket.read $1.to_i
- socket.read 2 # "\r\n"
- socket.gets # "END\r\n"
- return value
end
##
# Fetches +cache_keys+ from +server+ using a multi-get.
def cache_get_multi(server, cache_keys)
- values = {}
- socket = server.socket
- socket.write "get #{cache_keys}\r\n"
+ with_socket_management(server) do |socket|
+ values = {}
+ socket.write "get #{cache_keys}\r\n"
- while keyline = socket.gets do
- return values if keyline == "END\r\n"
- raise_on_error_response! keyline
+ while keyline = socket.gets do
+ return values if keyline == "END\r\n"
+ raise_on_error_response! keyline
- unless keyline =~ /\AVALUE (.+) (.+) (.+)/ then
- server.close
- raise MemCacheError, "unexpected response #{keyline.inspect}"
+ unless keyline =~ /\AVALUE (.+) (.+) (.+)/ then
+ server.close
+ raise MemCacheError, "unexpected response #{keyline.inspect}"
+ end
+
+ key, data_length = $1, $3
+ values[$1] = socket.read data_length.to_i
+ socket.read(2) # "\r\n"
end
- key, data_length = $1, $3
- values[$1] = socket.read data_length.to_i
- socket.read(2) # "\r\n"
+ server.close
+ raise MemCacheError, "lost connection to #{server.host}:#{server.port}" # TODO: retry here too
end
-
- server.close
- raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
end
##
@@ -634,18 +584,76 @@ class MemCache
# found.
def cache_incr(server, cache_key, amount)
- socket = server.socket
- socket.write "incr #{cache_key} #{amount}\r\n"
- text = socket.gets
- raise_on_error_response! text
- return nil if text == "NOT_FOUND\r\n"
- return text.to_i
+ with_socket_management(server) do |socket|
+ socket.write "incr #{cache_key} #{amount}\r\n"
+ text = socket.gets
+ raise_on_error_response! text
+ return nil if text == "NOT_FOUND\r\n"
+ return text.to_i
+ end
+ end
+
+ ##
+ # Gets or creates a socket connected to the given server, and yields it
+ # to the block, wrapped in a mutex synchronization if @multithread is true.
+ #
+ # If a socket error (SocketError, SystemCallError, IOError) or protocol error
+ # (MemCacheError) is raised by the block, closes the socket, attempts to
+ # connect again, and retries the block (once). If an error is again raised,
+ # reraises it as MemCacheError.
+ #
+ # If unable to connect to the server (or if in the reconnect wait period),
+ # raises MemCacheError. Note that the socket connect code marks a server
+ # dead for a timeout period, so retrying does not apply to connection attempt
+ # failures (but does still apply to unexpectedly lost connections etc.).
+
+ def with_socket_management(server, &block)
+ @mutex.lock if @multithread
+ retried = false
+
+ begin
+ socket = server.socket
+
+ # Raise an IndexError to show this server is out of whack. If were inside
+ # a with_server block, we'll catch it and attempt to restart the operation.
+
+ raise IndexError, "No connection to server (#{server.status})" if socket.nil?
+
+ block.call(socket)
+
+ rescue SocketError => err
+ server.mark_dead(err.message)
+ handle_error(server, err)
+
+ rescue MemCacheError, SocketError, SystemCallError, IOError => err
+ handle_error(server, err) if retried || socket.nil?
+ retried = true
+ retry
+ end
+ ensure
+ @mutex.unlock if @multithread
+ end
+
+ def with_server(key)
+ retried = false
+ begin
+ server, cache_key = request_setup(key)
+ yield server, cache_key
+ rescue IndexError => e
+ if !retried && @servers.size > 1
+ puts "Connection to server #{server.inspect} DIED! Retrying operation..."
+ retried = true
+ retry
+ end
+ handle_error(nil, e)
+ end
end
##
# Handles +error+ from +server+.
def handle_error(server, error)
+ raise error if error.is_a?(MemCacheError)
server.close if server
new_error = MemCacheError.new error.message
new_error.set_backtrace error.backtrace
@@ -660,45 +668,15 @@ class MemCache
raise MemCacheError, 'No active servers' unless active?
cache_key = make_cache_key key
server = get_server_for_key cache_key
- raise MemCacheError, 'No connection to server' if server.socket.nil?
return server, cache_key
end
- def threadsafe_cache_decr(server, cache_key, amount) # :nodoc:
- @mutex.lock
- cache_decr server, cache_key, amount
- ensure
- @mutex.unlock
- end
-
- def threadsafe_cache_get(server, cache_key) # :nodoc:
- @mutex.lock
- cache_get server, cache_key
- ensure
- @mutex.unlock
- end
-
- def threadsafe_cache_get_multi(socket, cache_keys) # :nodoc:
- @mutex.lock
- cache_get_multi socket, cache_keys
- ensure
- @mutex.unlock
- end
-
- def threadsafe_cache_incr(server, cache_key, amount) # :nodoc:
- @mutex.lock
- cache_incr server, cache_key, amount
- ensure
- @mutex.unlock
- end
-
def raise_on_error_response!(response)
- if response =~ /\A(?:CLIENT_|SERVER_)?ERROR (.*)/
+ if response =~ /\A(?:CLIENT_|SERVER_)?ERROR(.*)/
raise MemCacheError, $1.strip
end
end
-
##
# This class represents a memcached server instance.
@@ -712,6 +690,13 @@ class MemCache
CONNECT_TIMEOUT = 0.25
##
+ # The amount of time to wait for a response from a memcached server.
+ # If a response isn't received within this time limit,
+ # the server will be marked as down.
+
+ SOCKET_TIMEOUT = 0.5
+
+ ##
# The amount of time to wait before attempting to re-establish a
# connection with a server that is marked dead.
@@ -795,9 +780,9 @@ class MemCache
# Attempt to connect if not already connected.
begin
- @sock = timeout CONNECT_TIMEOUT do
- TCPSocket.new @host, @port
- end
+
+ @sock = TCPTimeoutSocket.new @host, @port
+
if Socket.constants.include? 'TCP_NODELAY' then
@sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
end
@@ -826,8 +811,6 @@ class MemCache
@mutex.unlock if @multithread
end
- private
-
##
# Mark the server as dead and close its socket.
@@ -836,8 +819,9 @@ class MemCache
@sock = nil
@retry = Time.now + RETRY_DELAY
- @status = sprintf "DEAD: %s, will retry at %s", reason, @retry
+ @status = sprintf "%s:%s DEAD: %s, will retry at %s", @host, @port, reason, @retry
end
+
end
##
@@ -847,3 +831,38 @@ class MemCache
end
+# TCPSocket facade class which implements timeouts.
+class TCPTimeoutSocket
+ def initialize(*args)
+ Timeout::timeout(MemCache::Server::CONNECT_TIMEOUT, SocketError) do
+ @sock = TCPSocket.new(*args)
+ @len = MemCache::Server::SOCKET_TIMEOUT.to_f || 0.5
+ end
+ end
+
+ def write(*args)
+ Timeout::timeout(@len, SocketError) do
+ @sock.write(*args)
+ end
+ end
+
+ def gets(*args)
+ Timeout::timeout(@len, SocketError) do
+ @sock.gets(*args)
+ end
+ end
+
+ def read(*args)
+ Timeout::timeout(@len, SocketError) do
+ @sock.read(*args)
+ end
+ end
+
+ def _socket
+ @sock
+ end
+
+ def method_missing(meth, *args)
+ @sock.__send__(meth, *args)
+ end
+end \ No newline at end of file
diff --git a/activesupport/test/buffered_logger_test.rb b/activesupport/test/buffered_logger_test.rb
index 28dd34334f..e178ced06d 100644
--- a/activesupport/test/buffered_logger_test.rb
+++ b/activesupport/test/buffered_logger_test.rb
@@ -137,4 +137,10 @@ class BufferedLoggerTest < Test::Unit::TestCase
assert @output.string.include?("a\nb\nc\n")
assert @output.string.include?("x\ny\nz\n")
end
+
+ def test_flush_should_remove_empty_buffers
+ @logger.send :buffer
+ @logger.expects :clear_buffer
+ @logger.flush
+ end
end
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index 25b8eecef5..2bc2e1eaf0 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -53,10 +53,41 @@ class Person < Record
end
class ConditionalPerson < Record
+ # proc
before_save Proc.new { |r| r.history << [:before_save, :proc] }, :if => Proc.new { |r| true }
before_save Proc.new { |r| r.history << "b00m" }, :if => Proc.new { |r| false }
before_save Proc.new { |r| r.history << [:before_save, :proc] }, :unless => Proc.new { |r| false }
before_save Proc.new { |r| r.history << "b00m" }, :unless => Proc.new { |r| true }
+ # symbol
+ before_save Proc.new { |r| r.history << [:before_save, :symbol] }, :if => :yes
+ before_save Proc.new { |r| r.history << "b00m" }, :if => :no
+ before_save Proc.new { |r| r.history << [:before_save, :symbol] }, :unless => :no
+ before_save Proc.new { |r| r.history << "b00m" }, :unless => :yes
+ # string
+ before_save Proc.new { |r| r.history << [:before_save, :string] }, :if => 'yes'
+ before_save Proc.new { |r| r.history << "b00m" }, :if => 'no'
+ before_save Proc.new { |r| r.history << [:before_save, :string] }, :unless => 'no'
+ before_save Proc.new { |r| r.history << "b00m" }, :unless => 'yes'
+ # Array with conditions
+ before_save Proc.new { |r| r.history << [:before_save, :symbol_array] }, :if => [:yes, :other_yes]
+ before_save Proc.new { |r| r.history << "b00m" }, :if => [:yes, :no]
+ before_save Proc.new { |r| r.history << [:before_save, :symbol_array] }, :unless => [:no, :other_no]
+ before_save Proc.new { |r| r.history << "b00m" }, :unless => [:yes, :no]
+ # Combined if and unless
+ before_save Proc.new { |r| r.history << [:before_save, :combined_symbol] }, :if => :yes, :unless => :no
+ before_save Proc.new { |r| r.history << "b00m" }, :if => :yes, :unless => :yes
+ # Array with different types of conditions
+ before_save Proc.new { |r| r.history << [:before_save, :symbol_proc_string_array] }, :if => [:yes, Proc.new { |r| true }, 'yes']
+ before_save Proc.new { |r| r.history << "b00m" }, :if => [:yes, Proc.new { |r| true }, 'no']
+ # Array with different types of conditions comibned if and unless
+ before_save Proc.new { |r| r.history << [:before_save, :combined_symbol_proc_string_array] },
+ :if => [:yes, Proc.new { |r| true }, 'yes'], :unless => [:no, 'no']
+ before_save Proc.new { |r| r.history << "b00m" }, :if => [:yes, Proc.new { |r| true }, 'no'], :unless => [:no, 'no']
+
+ def yes; true; end
+ def other_yes; true; end
+ def no; false; end
+ def other_no; false; end
def save
run_callbacks(:before_save)
@@ -90,7 +121,16 @@ class ConditionalCallbackTest < Test::Unit::TestCase
person.save
assert_equal [
[:before_save, :proc],
- [:before_save, :proc]
+ [:before_save, :proc],
+ [:before_save, :symbol],
+ [:before_save, :symbol],
+ [:before_save, :string],
+ [:before_save, :string],
+ [:before_save, :symbol_array],
+ [:before_save, :symbol_array],
+ [:before_save, :combined_symbol],
+ [:before_save, :symbol_proc_string_array],
+ [:before_save, :combined_symbol_proc_string_array]
], person.history
end
end
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 30cbba26b0..b63ab30965 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -287,10 +287,14 @@ class HashExtTest < Test::Unit::TestCase
# Should return a new hash with only the given keys.
assert_equal expected, original.slice(:a, :b)
assert_not_equal expected, original
+ end
+
+ def test_slice_inplace
+ original = { :a => 'x', :b => 'y', :c => 10 }
+ expected = { :c => 10 }
# Should replace the hash with only the given keys.
assert_equal expected, original.slice!(:a, :b)
- assert_equal expected, original
end
def test_slice_with_an_array_key
@@ -300,10 +304,14 @@ class HashExtTest < Test::Unit::TestCase
# Should return a new hash with only the given keys when given an array key.
assert_equal expected, original.slice([:a, :b], :c)
assert_not_equal expected, original
+ end
+
+ def test_slice_inplace_with_an_array_key
+ original = { :a => 'x', :b => 'y', :c => 10, [:a, :b] => "an array key" }
+ expected = { :a => 'x', :b => 'y' }
# Should replace the hash with only the given keys when given an array key.
assert_equal expected, original.slice!([:a, :b], :c)
- assert_equal expected, original
end
def test_slice_with_splatted_keys
@@ -322,11 +330,17 @@ class HashExtTest < Test::Unit::TestCase
# Should return a new hash with only the given keys.
assert_equal expected, original.slice(*keys), keys.inspect
assert_not_equal expected, original
+ end
+ end
+
+ def test_indifferent_slice_inplace
+ original = { :a => 'x', :b => 'y', :c => 10 }.with_indifferent_access
+ expected = { :c => 10 }.with_indifferent_access
+ [['a', 'b'], [:a, :b]].each do |keys|
# Should replace the hash with only the given keys.
copy = original.dup
assert_equal expected, copy.slice!(*keys)
- assert_equal expected, copy
end
end
@@ -403,6 +417,13 @@ class HashToXmlTest < Test::Unit::TestCase
assert xml.include?(%(<name>David</name>))
end
+ def test_one_level_camelize_true
+ xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:camelize => true))
+ assert_equal "<Person>", xml.first(8)
+ assert xml.include?(%(<StreetName>Paulina</StreetName>))
+ assert xml.include?(%(<Name>David</Name>))
+ end
+
def test_one_level_with_types
xml = { :name => "David", :street => "Paulina", :age => 26, :age_in_millis => 820497600000, :moved_on => Date.new(2005, 11, 15), :resident => :yes }.to_xml(@xml_options)
assert_equal "<person>", xml.first(8)
diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb
index 886f692499..a5d98507ba 100644
--- a/activesupport/test/core_ext/module_test.rb
+++ b/activesupport/test/core_ext/module_test.rb
@@ -41,6 +41,10 @@ Invoice = Struct.new(:client) do
delegate :street, :city, :name, :to => :client, :prefix => :customer
end
+Project = Struct.new(:description, :person) do
+ delegate :name, :to => :person, :allow_nil => true
+end
+
class Name
delegate :upcase, :to => :@full_name
@@ -117,6 +121,29 @@ class ModuleTest < Test::Unit::TestCase
end
end
+ def test_delegation_with_allow_nil
+ rails = Project.new("Rails", Someone.new("David"))
+ assert_equal rails.name, "David"
+ end
+
+ def test_delegation_with_allow_nil_and_nil_value
+ rails = Project.new("Rails")
+ assert_nil rails.name
+ end
+
+ def test_delegation_with_allow_nil_and_nil_value_and_prefix
+ Project.class_eval do
+ delegate :name, :to => :person, :allow_nil => true, :prefix => true
+ end
+ rails = Project.new("Rails")
+ assert_nil rails.person_name
+ end
+
+ def test_delegation_without_allow_nil_and_nil_value
+ david = Someone.new("David")
+ assert_raises(NoMethodError) { david.street }
+ end
+
def test_parent
assert_equal Yz::Zy, Yz::Zy::Cd.parent
assert_equal Yz, Yz::Zy.parent
diff --git a/activesupport/test/core_ext/object_ext_test.rb b/activesupport/test/core_ext/object_ext_test.rb
new file mode 100644
index 0000000000..a413d331c4
--- /dev/null
+++ b/activesupport/test/core_ext/object_ext_test.rb
@@ -0,0 +1,8 @@
+require 'abstract_unit'
+
+class ObjectExtTest < Test::Unit::TestCase
+ def test_tap_yields_and_returns_self
+ foo = Object.new
+ assert_equal foo, foo.tap { |x| assert_equal foo, x; :bar }
+ end
+end
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index dc36336239..7c8fb7dd94 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -256,6 +256,15 @@ class TimeWithZoneTest < Test::Unit::TestCase
twz2 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone['UTC'] )
assert_equal 86_400.0, twz2 - twz1
end
+
+ def test_minus_with_datetime
+ assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone['UTC'] ) - DateTime.civil(2000, 1, 1)
+ end
+
+ def test_minus_with_wrapped_datetime
+ assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( DateTime.civil(2000, 1, 2), ActiveSupport::TimeZone['UTC'] ) - Time.utc(2000, 1, 1)
+ assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( DateTime.civil(2000, 1, 2), ActiveSupport::TimeZone['UTC'] ) - DateTime.civil(2000, 1, 1)
+ end
def test_plus_and_minus_enforce_spring_dst_rules
silence_warnings do # silence warnings raised by tzinfo gem
diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb
index 19ae3a01a8..558b03b90d 100644
--- a/activesupport/test/json/decoding_test.rb
+++ b/activesupport/test/json/decoding_test.rb
@@ -15,7 +15,8 @@ class TestJSONDecoding < Test::Unit::TestCase
# no time zone
%({a: "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"},
# needs to be *exact*
- %({a: " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "},
+ %({a: " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "},
+ %({a: "2007-01-01 : it's your birthday"}) => {'a' => "2007-01-01 : it's your birthday"},
%([]) => [],
%({}) => {},
%(1) => 1,
diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb
index 17dffbd624..0e2aa4543d 100644
--- a/activesupport/test/ordered_hash_test.rb
+++ b/activesupport/test/ordered_hash_test.rb
@@ -36,9 +36,11 @@ class OrderedHashTest < Test::Unit::TestCase
@ordered_hash[key] = value
assert_equal @keys.length + 1, @ordered_hash.length
+ assert_equal @ordered_hash.keys.length, @ordered_hash.length
assert_equal value, @ordered_hash.delete(key)
assert_equal @keys.length, @ordered_hash.length
+ assert_equal @ordered_hash.keys.length, @ordered_hash.length
assert_nil @ordered_hash.delete(bad_key)
end
@@ -73,4 +75,69 @@ class OrderedHashTest < Test::Unit::TestCase
@ordered_hash.each_value { |v| values << v }
assert_equal @values, values
end
-end
+
+ def test_each
+ values = []
+ @ordered_hash.each {|key, value| values << value}
+ assert_equal @values, values
+ end
+
+ def test_each_with_index
+ @ordered_hash.each_with_index { |pair, index| assert_equal [@keys[index], @values[index]], pair}
+ end
+
+ def test_each_pair
+ values = []
+ keys = []
+ @ordered_hash.each_pair do |key, value|
+ keys << key
+ values << value
+ end
+ assert_equal @values, values
+ assert_equal @keys, keys
+ end
+
+ def test_delete_if
+ (copy = @ordered_hash.dup).delete('pink')
+ assert_equal copy, @ordered_hash.delete_if { |k, _| k == 'pink' }
+ assert !@ordered_hash.keys.include?('pink')
+ end
+
+ def test_reject!
+ (copy = @ordered_hash.dup).delete('pink')
+ @ordered_hash.reject! { |k, _| k == 'pink' }
+ assert_equal copy, @ordered_hash
+ assert !@ordered_hash.keys.include?('pink')
+ end
+
+ def test_reject
+ copy = @ordered_hash.dup
+ new_ordered_hash = @ordered_hash.reject { |k, _| k == 'pink' }
+ assert_equal copy, @ordered_hash
+ assert !new_ordered_hash.keys.include?('pink')
+ end
+
+ def test_clear
+ @ordered_hash.clear
+ assert_equal [], @ordered_hash.keys
+ end
+
+ def test_merge
+ other_hash = ActiveSupport::OrderedHash.new
+ other_hash['purple'] = '800080'
+ other_hash['violet'] = 'ee82ee'
+ merged = @ordered_hash.merge other_hash
+ assert_equal merged.length, @ordered_hash.length + other_hash.length
+ assert_equal @keys + ['purple', 'violet'], merged.keys
+
+ @ordered_hash.merge! other_hash
+ assert_equal @ordered_hash, merged
+ assert_equal @ordered_hash.keys, merged.keys
+ end
+
+ def test_shift
+ pair = @ordered_hash.shift
+ assert_equal [@keys.first, @values.first], pair
+ assert !@ordered_hash.keys.include?(pair.first)
+ end
+end \ No newline at end of file
diff --git a/ci/ci_build.rb b/ci/ci_build.rb
index 7b9cdceb27..010f78ba09 100755
--- a/ci/ci_build.rb
+++ b/ci/ci_build.rb
@@ -23,6 +23,7 @@ cd "#{root_dir}/activesupport" do
build_results[:activesupport] = system 'rake'
end
+rm_f "#{root_dir}/activerecord/debug.log"
cd "#{root_dir}/activerecord" do
puts
puts "[CruiseControl] Building ActiveRecord with MySQL"
@@ -37,13 +38,12 @@ cd "#{root_dir}/activerecord" do
build_results[:activerecord_postgresql8] = system 'rake test_postgresql'
end
-# Sqlite2 is disabled until tests are fixed
-# cd "#{root_dir}/activerecord" do
-# puts
-# puts "[CruiseControl] Building ActiveRecord with SQLite 2"
-# puts
-# build_results[:activerecord_sqlite] = system 'rake test_sqlite'
-# end
+cd "#{root_dir}/activerecord" do
+ puts
+ puts "[CruiseControl] Building ActiveRecord with SQLite 2"
+ puts
+ build_results[:activerecord_sqlite] = system 'rake test_sqlite'
+end
cd "#{root_dir}/activerecord" do
puts
@@ -59,6 +59,7 @@ cd "#{root_dir}/activemodel" do
build_results[:activemodel] = system 'rake'
end
+rm_f "#{root_dir}/activeresource/debug.log"
cd "#{root_dir}/activeresource" do
puts
puts "[CruiseControl] Building ActiveResource"
diff --git a/ci/ci_setup_notes.txt b/ci/ci_setup_notes.txt
index 86df33c443..b3c5936797 100644
--- a/ci/ci_setup_notes.txt
+++ b/ci/ci_setup_notes.txt
@@ -54,10 +54,14 @@ ci ALL=NOPASSWD: /usr/local/bin/geminstaller, /usr/local/bin/ruby, /usr/loc
* Install/setup nginx:
$ sudo aptitude install nginx
$ sudo vi /etc/nginx/sites-available/default
+# Change server_name entry to match server name
+
# comment two lines and add one to proxy to ccrb:
# root /var/www/nginx-default;
# index index.html index.htm;
proxy_pass http://127.0.0.1:3333;
+
+# also comment default locations for /doc and /images
$ sudo /etc/init.d/nginx start
* Add project to cruise (It will still fail until everything is set up):
@@ -101,6 +105,13 @@ $ sudo aptitude install sqlite sqlite3 libsqlite-dev libsqlite3-dev
$ sudo aptitude install postgresql postgresql-server-dev-8.3
$ sudo su - postgres -c 'createuser -s ci'
+* Install fcgi libraries
+$ sudo apt-get install libfcgi-dev
+
+* Install memcached and start for first time (should start on reboot automatically)
+$ sudo aptitude install memcached
+$ sudo /etc/init.d/memcached start
+
* Install and run GemInstaller to get all dependency gems
$ sudo gem install geminstaller
$ cd ~/.cruise/projects/rails/work
diff --git a/ci/cruise_config.rb b/ci/cruise_config.rb
index 46325f5ebd..325c21397e 100644
--- a/ci/cruise_config.rb
+++ b/ci/cruise_config.rb
@@ -1,5 +1,6 @@
Project.configure do |project|
project.build_command = 'ruby ci/ci_build.rb'
- project.email_notifier.emails = ['thewoolleyman@gmail.com','michael@koziarski.com', 'david@loudthinking.com', 'jeremy@bitsweat.net', 'josh@joshpeek.com', 'pratiknaik@gmail.com']
+# project.email_notifier.emails = ['thewoolleyman@gmail.com']
+ project.email_notifier.emails = ['thewoolleyman@gmail.com','michael@koziarski.com', 'david@loudthinking.com', 'jeremy@bitsweat.net', 'josh@joshpeek.com', 'pratiknaik@gmail.com', 'wycats@gmail.com']
project.email_notifier.from = 'thewoolleyman+railsci@gmail.com'
end
diff --git a/ci/geminstaller.yml b/ci/geminstaller.yml
index 3a1862c3c3..4251518999 100644
--- a/ci/geminstaller.yml
+++ b/ci/geminstaller.yml
@@ -2,13 +2,19 @@
gems:
- name: geminstaller
version: >= 0.4.3
+- name: fcgi
+ version: >= 0.8.7
+- name: memcache-client
+ version: >= 1.5.0
- name: mocha
- version: >= 0.9.0
+ version: >= 0.9.4
- name: mysql
#version: >= 2.7
version: = 2.7
- name: postgres
version: >= 0.7.9.2008.01.28
+- name: rack
+ version: '~> 0.9.0'
- name: rake
version: >= 0.8.1
- name: sqlite-ruby
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index 9ce7b6349c..b313f082a3 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,6 +1,6 @@
*2.3.0 [Edge]*
-* Add a rake task to apply a template to an existing application : rake rails:template LOCATION=~/template.rb [Pratik]
+* Add a rake task to apply a template to an existing application : rake rails:template LOCATION=~/template.rb [Pratik Naik]
* Add "-m/--template" option to Rails generator to apply a template to the generated application. [Jeremy McAnally]
@@ -45,31 +45,31 @@
rails <application name> --template=http://gist.github.com/31208.txt
-* Extracted the process scripts (inspector, reaper, spawner) into the plugin irs_process_scripts [DHH]
+* Extracted the process scripts (inspector, reaper, spawner) into the plugin irs_process_scripts [David Heinemeier Hansson]
* Changed Rails.root to return a Pathname object (allows for Rails.root.join('app', 'controllers') => "#{RAILS_ROOT}/app/controllers") #1482 [Damian Janowski/?]
-* Added view path support for engines [DHH]
+* Added view path support for engines [David Heinemeier Hansson]
-* Added that config/routes.rb files in engine plugins are automatically loaded (and reloaded when they change in dev mode) [DHH]
+* Added that config/routes.rb files in engine plugins are automatically loaded (and reloaded when they change in dev mode) [David Heinemeier Hansson]
-* Added app/[models|controllers|helpers] to the load path for plugins that has an app directory (go engines ;)) [DHH]
+* Added app/[models|controllers|helpers] to the load path for plugins that has an app directory (go engines ;)) [David Heinemeier Hansson]
* Add config.preload_frameworks to load all frameworks at startup. Default to false so Rails autoloads itself as it's used. Turn this on for Passenger and JRuby. Also turned on by config.threadsafe! [Jeremy Kemper]
-* Add a rake task to generate dispatchers : rake rails:generate_dispatchers [Pratik]
+* Add a rake task to generate dispatchers : rake rails:generate_dispatchers [Pratik Naik]
* "rails <app>" will not generate public/dispatch.cgi/fcgi/rb files by default now. Please use "--with-dispatchers" option if you need them. [Yaroslav Markin, Pratik Naik]
* Added rake rails:update:application_controller to renamed application.rb to application_controller.rb -- included in rake rails:update so upgrading to 2.3 will automatically trigger it #1439 [kastner]
-* Added Rails.backtrace_cleaner as an accessor for the Rails::BacktraceCleaner instance used by the framework to cut down on backtrace noise and config/initializers/backtrace_silencers.rb to add your own (or turn them all off) [DHH]
+* Added Rails.backtrace_cleaner as an accessor for the Rails::BacktraceCleaner instance used by the framework to cut down on backtrace noise and config/initializers/backtrace_silencers.rb to add your own (or turn them all off) [David Heinemeier Hansson]
* Switch from Test::Unit::TestCase to ActiveSupport::TestCase. [Jeremy Kemper]
-* Added config.i18n settings gatherer to config/environment, auto-loading of all locales in config/locales/*.rb,yml, and config/locales/en.yml as a sample locale [DHH]
+* Added config.i18n settings gatherer to config/environment, auto-loading of all locales in config/locales/*.rb,yml, and config/locales/en.yml as a sample locale [David Heinemeier Hansson]
-* BACKWARDS INCOMPATIBLE: Renamed application.rb to application_controller.rb and removed all the special casing that was in place to support the former. You must do this rename in your own application when you upgrade to this version [DHH]
+* BACKWARDS INCOMPATIBLE: Renamed application.rb to application_controller.rb and removed all the special casing that was in place to support the former. You must do this rename in your own application when you upgrade to this version [David Heinemeier Hansson]
*2.2.1 [RC2] (November 14th, 2008)*
@@ -110,9 +110,9 @@
* Plugins check for the gem init path (rails/init.rb) before the standard plugin init path (init.rb) [Jacek Becela]
-* Changed all generated tests to use the test/do declaration style [DHH]
+* Changed all generated tests to use the test/do declaration style [David Heinemeier Hansson]
-* Wrapped Rails.env in StringInquirer so you can do Rails.env.development? [DHH]
+* Wrapped Rails.env in StringInquirer so you can do Rails.env.development? [David Heinemeier Hansson]
* Fixed that RailsInfoController wasn't considering all requests local in development mode (Edgard Castro) [#310 state:resolved]
@@ -127,13 +127,13 @@
* Rails Edge info returns the latest git commit hash [Francesc Esplugas]
-* Added Rails.public_path to control where HTML and assets are expected to be loaded from (defaults to Rails.root + "/public") #11581 [nicksieger]
+* Added Rails.public_path to control where HTML and assets are expected to be loaded from (defaults to Rails.root + "/public") #11581 [Nick Sieger]
* rake time:zones:local finds correct base utc offset for zones in the Southern Hemisphere [Geoff Buesing]
-* Don't require rails/gem_builder during rails initialization, it's only needed for the gems:build task. [rick]
+* Don't require rails/gem_builder during rails initialization, it's only needed for the gems:build task. [Rick Olson]
-* script/performance/profiler compatibility with the ruby-prof >= 0.5.0. Closes #9176. [Catfish]
+* script/performance/profiler compatibility with the ruby-prof >= 0.5.0. Closes #9176. [Jonathan del Strother]
* Flesh out rake gems:unpack to unpack all gems, and add rake gems:build for native extensions. #11513 [ddollar]
@@ -143,11 +143,11 @@
rake gems:build # builds all unpacked gems
rake gems:build GEM=mygem # builds only the gem 'mygem'
-* Add config.active_support for future configuration options. Also, add more new Rails 3 config settings to new_rails_defaults.rb [rick]
+* Add config.active_support for future configuration options. Also, add more new Rails 3 config settings to new_rails_defaults.rb [Rick Olson]
-* Add Rails.logger, Rails.root, Rails.env and Rails.cache shortcuts for RAILS_* constants [pratik]
+* Add Rails.logger, Rails.root, Rails.env and Rails.cache shortcuts for RAILS_* constants [Pratik Naik]
-* Allow files in plugins to be reloaded like the rest of the application. [rick]
+* Allow files in plugins to be reloaded like the rest of the application. [Rick Olson]
Enables or disables plugin reloading.
@@ -162,7 +162,7 @@
Dependencies.load_once_paths << lib_path
-* Small tweak to allow plugins to specify gem dependencies. [rick]
+* Small tweak to allow plugins to specify gem dependencies. [Rick Olson]
# OLD open_id_authentication plugin init.rb
require 'yadis'
@@ -177,7 +177,7 @@
ActionController::Base.send :include, OpenIdAuthentication
end
-* Added config.gem for specifying which gems are required by the application, as well as rake tasks for installing and freezing gems. [rick]
+* Added config.gem for specifying which gems are required by the application, as well as rake tasks for installing and freezing gems. [Rick Olson]
Rails::Initializer.run do |config|
config.gem "bj"
@@ -194,23 +194,23 @@
# Unpack specified gem to vendor/gems/gem_name-x.x.x
rake gems:unpack GEM=bj
-* Removed the default .htaccess configuration as there are so many good deployment options now (kept it as an example in README) [DHH]
+* Removed the default .htaccess configuration as there are so many good deployment options now (kept it as an example in README) [David Heinemeier Hansson]
* config.time_zone accepts TZInfo::Timezone identifiers as well as Rails TimeZone identifiers [Geoff Buesing]
* Rails::Initializer#initialize_time_zone raises an error if value assigned to config.time_zone is not recognized. Rake time zone tasks only require ActiveSupport instead of entire environment [Geoff Buesing]
-* Stop adding the antiquated test/mocks/* directories and only add them to the path if they're still there for legacy reasons [DHH]
+* Stop adding the antiquated test/mocks/* directories and only add them to the path if they're still there for legacy reasons [David Heinemeier Hansson]
-* Added that gems can now be plugins if they include rails/init.rb #11444 [jbarnette]
+* Added that gems can now be plugins if they include rails/init.rb #11444 [John Barnette]
-* Added Plugin#about method to programmatically access the about.yml in a plugin #10979 [lazyatom]
+* Added Plugin#about method to programmatically access the about.yml in a plugin #10979 [James Adam]
plugin = Rails::Plugin.new(path_to_my_plugin)
plugin.about["author"] # => "James Adam"
plugin.about["url"] # => "http://interblah.net"
-* Improve documentation. [Radar, Jan De Poorter, chuyeow, xaviershay, danger, miloops, Xavier Noria, Sunny Ripert]
+* Improve documentation. [Ryan Bigg, Jan De Poorter, Cheah Chu Yeow, Xavier Shay, Jack Danger Canty, Emilio Tagua, Xavier Noria, Sunny Ripert]
* Added config.time_zone = 'UTC' in the default environment.rb [Geoff Buesing]
@@ -218,7 +218,7 @@
* Add config.time_zone for configuring the default Time.zone value. #10982 [Geoff Buesing]
-* Added support for installing plugins hosted at git repositories #11294 [danger]
+* Added support for installing plugins hosted at git repositories #11294 [Jack Danger Canty]
* Fixed that script/generate would not look for plugin generators in plugin_paths #11000 [glv]
@@ -226,7 +226,7 @@
* Added a -e/--export to script/plugin install, uses svn export. #10847 [jon@blankpad.net)]
-* Reshuffle load order so that routes and observers are initialized after plugins and app initializers. Closes #10980 [rick]
+* Reshuffle load order so that routes and observers are initialized after plugins and app initializers. Closes #10980 [Rick Olson]
* Git support for script/generate. #10690 [ssoroka]
@@ -234,7 +234,7 @@
* Resurrect WordNet synonym lookups. #10710 [tom./, matt]
-* Added config.cache_store to environment options to control the default cache store (default is FileStore if tmp/cache is present, otherwise MemoryStore is used) [DHH]
+* Added config.cache_store to environment options to control the default cache store (default is FileStore if tmp/cache is present, otherwise MemoryStore is used) [David Heinemeier Hansson]
* Added that rails:update is run when you do rails:freeze:edge to ensure you also get the latest JS and config files #10565 [jeff]
@@ -247,9 +247,9 @@
*2.0.2* (December 16th, 2007)
-* Changed the default database from mysql to sqlite3, so now running "rails myapp" will have a config/database.yml that's setup for SQLite3 (which in OS X Leopard is installed by default, so is the gem, so everything Just Works with no database configuration at all). To get a Rails application preconfigured for MySQL, just run "rails -d mysql myapp" [DHH]
+* Changed the default database from mysql to sqlite3, so now running "rails myapp" will have a config/database.yml that's setup for SQLite3 (which in OS X Leopard is installed by default, so is the gem, so everything Just Works with no database configuration at all). To get a Rails application preconfigured for MySQL, just run "rails -d mysql myapp" [David Heinemeier Hansson]
-* Turned on ActionView::Base.cache_template_loading by default in config/environments/production.rb to prevent file system stat calls for every template loading to see if it changed (this means that you have to restart the application to see template changes in production mode) [DHH]
+* Turned on ActionView::Base.cache_template_loading by default in config/environments/production.rb to prevent file system stat calls for every template loading to see if it changed (this means that you have to restart the application to see template changes in production mode) [David Heinemeier Hansson]
* Introduce `rake secret` to output a crytographically secure secret key for use with cookie sessions #10363 [revans]
@@ -273,19 +273,19 @@
* The test task stops with a warning if you have pending migrations. #10377 [Josh Knowles]
-* Add warning to documentation about using transactional fixtures when the code under test uses transactions itself. Closes #7548 [thijsv]
+* Add warning to documentation about using transactional fixtures when the code under test uses transactions itself. Closes #7548 [Thijs van der Vossen]
* Update Prototype to 1.6.0.1. [sam]
* Update script.aculo.us to 1.8.0.1. [madrobby]
-* Added db:fixtures:identity as a way of locating what ID a foxy fixture was assigned #10332 [jbarnette]
+* Added db:fixtures:identity as a way of locating what ID a foxy fixture was assigned #10332 [John Barnette]
-* Generated fixtures should not specify ids since theyre expected to be foxy fixtures #10330 [jbarnette]
+* Generated fixtures should not specify ids since theyre expected to be foxy fixtures #10330 [John Barnette]
* Update to Prototype -r8232. [sam]
-* Introduce SecretKeyGenerator for more secure session secrets than CGI::Session's pseudo-random id generator. Consider extracting to Active Support later. #10286 [Hongli Lai]
+* Introduce SecretKeyGenerator for more secure session secrets than CGI::Session's pseudo-random id generator. Consider extracting to Active Support later. #10286 [Hongli Lai (Phusion)]
* RAILS_GEM_VERSION may be set to any valid gem version specifier. #10057 [Chad Woolley, Cheah Chu Yeow]
@@ -295,11 +295,11 @@
* Only load ActionMailer::TestCase if ActionMailer is loaded. Closes #10137 [defunkt]
-* Fixed that db:reset would use migrations instead of loading db/schema.rb [DHH]
+* Fixed that db:reset would use migrations instead of loading db/schema.rb [David Heinemeier Hansson]
* Ensure the plugin loader only loads plugins once. Closes #10102 [haruki_zaemon]
-* Refactor Plugin Loader. Add plugin lib paths early, and add lots of tests. Closes #9795 [lazyatom]
+* Refactor Plugin Loader. Add plugin lib paths early, and add lots of tests. Closes #9795 [James Adam]
* Added --skip-timestamps to generators that produce models #10036 [Tim Pope]
@@ -313,7 +313,7 @@
* config/boot.rb correctly detects RAILS_GEM_VERSION. #9834 [alexch, thewoolleyman]
-* Fixed incorrect migration number if script/generate executed outside of Rails root #7080 [jeremymcanally]
+* Fixed incorrect migration number if script/generate executed outside of Rails root #7080 [Jeremy McAnally]
* Update Prototype to 1.6.0_rc1 and script.aculo.us to 1.8.0 preview 0. [sam, madrobby]
@@ -326,7 +326,7 @@
* db:create works with remote databases whereas db:create:all only creates
databases on localhost. #9753 [Trevor Wennblom]
-* Removed calls to fixtures in generated tests as fixtures :all is now present by default in test_helper.rb [DHH]
+* Removed calls to fixtures in generated tests as fixtures :all is now present by default in test_helper.rb [David Heinemeier Hansson]
* Add --prefix option to script/server when using mongrel. [dacat]
@@ -335,13 +335,13 @@ databases on localhost. #9753 [Trevor Wennblom]
* Fixed that installing plugins from SVN repositories that use trunk/ will work #8188 [evan]
-* Moved the SourceAnnotationExtractor to a separate file in case libraries try to load the rails rake tasks twice. [Rick]
+* Moved the SourceAnnotationExtractor to a separate file in case libraries try to load the rails rake tasks twice. [Rick Olson]
* Moved Dispatcher to ActionController::Dispatcher. [Jeremy Kemper]
-* Changed the default logger from Ruby's own Logger with the clean_logger extensions to ActiveSupport::BufferedLogger for performance reasons [DHH]. (You can change it back with config.logger = Logger.new("/path/to/log", level).)
+* Changed the default logger from Ruby's own Logger with the clean_logger extensions to ActiveSupport::BufferedLogger for performance reasons [David Heinemeier Hansson]. (You can change it back with config.logger = Logger.new("/path/to/log", level).)
-* Added a default 422.html page to be rendered when ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved, or ActionController::InvalidAuthenticityToken is raised [DHH]
+* Added a default 422.html page to be rendered when ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved, or ActionController::InvalidAuthenticityToken is raised [David Heinemeier Hansson]
* Added --skip-fixture option to script/generate model #6862 [sandofsky]
@@ -349,15 +349,15 @@ databases on localhost. #9753 [Trevor Wennblom]
* Fixed the placement of fixture files for nested models when generating through script/generate model #7547 [jkit]
-* Added TEMPLATE option to rake doc:app to set a custom output template #7737 [Jakob S]
+* Added TEMPLATE option to rake doc:app to set a custom output template #7737 [Jakob Skjerning]
-* Added VERBOSE option to rake db:migrate to turn off output #8204 [jbarnette]
+* Added VERBOSE option to rake db:migrate to turn off output #8204 [John Barnette]
* Fixed that rake doc:app should use UTF-8 #8906 [farzy]
* Fixes rake annotations to search erb and builder files as well #9150 [m.langenberg]
-* Removed web_service generator [Koz]
+* Removed web_service generator [Michael Koziarski]
* Added the :all option to config.plugins that'll include the rest of the plugins not already explicitly named #9613 [Frederick Cheung]. Example:
@@ -366,11 +366,11 @@ databases on localhost. #9753 [Trevor Wennblom]
* Added symbols as a legal way of specifying plugins in config.plugins #9629 [tom]
-* Removed deprecated task names, like clear_logs, in favor of the new namespaced style [DHH]
+* Removed deprecated task names, like clear_logs, in favor of the new namespaced style [David Heinemeier Hansson]
-* Support multiple config.after_initialize blocks so plugins and apps can more easily cooperate. #9582 [zdennis]
+* Support multiple config.after_initialize blocks so plugins and apps can more easily cooperate. #9582 [Zach Dennis]
-* Added db:drop:all to drop all databases declared in config/database.yml [DHH]
+* Added db:drop:all to drop all databases declared in config/database.yml [David Heinemeier Hansson]
* Use attribute pairs instead of the migration name to create add and remove column migrations. Closes #9166 [Pratik Naik]
@@ -382,11 +382,11 @@ databases on localhost. #9753 [Trevor Wennblom]
ruby script/generation migration RemoveSomeStuffFromCustomers first_name:string last_name:string
-* Add ActiveResource to Rails::Info. Closes #8741 [kampers]
+* Add ActiveResource to Rails::Info. Closes #8741 [Chris Kampmeier]
* use Gem.find_name instead of search when freezing gems. Prevent false positives for other gems with rails in the name. Closes #8729 [wselman]
-* Automatically generate add/remove column commands in specially named migrations like AddLocationToEvent. Closes #9006 [zenspider]
+* Automatically generate add/remove column commands in specially named migrations like AddLocationToEvent. Closes #9006 [Ryan Davis]
* Default to plural table name in Rails Generator if ActiveRecord is not present. Closes #8963 [evan]
@@ -404,7 +404,7 @@ databases on localhost. #9753 [Trevor Wennblom]
* Fix that FCGIs would leave log files open when asked to shut down by USR2. #3028 [Sebastian Kanthak, Josh Peek]
-* Fixed that dispatcher preparation callbacks only run once in production mode. Mock Routes.reload so that dispatcher preparation callback tests run. [Rick]
+* Fixed that dispatcher preparation callbacks only run once in production mode. Mock Routes.reload so that dispatcher preparation callback tests run. [Rick Olson]
* Fix syntax error in dispatcher than wrecked failsafe responses. #8625 [mtitorenko]
@@ -432,11 +432,11 @@ databases on localhost. #9753 [Trevor Wennblom]
* Generators use *.html.erb view template naming. #8278 [Tim Pope]
-* Updated resource_scaffold and model generators to use short-hand style migrations [DHH]
+* Updated resource_scaffold and model generators to use short-hand style migrations [David Heinemeier Hansson]
* Updated initializer to only load #{RAILS_ENV}.rb once. Added deprecation warning for config.breakpoint_server. [Nicholas Seckar]
-* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back [DHH]
+* Removed breakpointer and Binding.of_caller in favor of relying on ruby-debug by Kent Sibilev since the breakpointer has been broken since Ruby 1.8.4 and will not be coming back [David Heinemeier Hansson]
To use the new debugger, start your server with script/server --debugger and insert a call to 'debugger'
(instead of 'breakpoint') where you want to jump into the debugger.
@@ -454,13 +454,13 @@ databases on localhost. #9753 [Trevor Wennblom]
* Include Active Resource in rails:freeze:edge rake task. [Thomas Fuchs]
-* Include Active Resource instead of Action Web Service [DHH] You can add AWS back with this in config/environment.rb:
+* Include Active Resource instead of Action Web Service [David Heinemeier Hansson] You can add AWS back with this in config/environment.rb:
config.load_paths += %W( #{RAILS_ROOT}/vendor/rails/actionwebservice/lib )
...or just gem 'actionwebservice'
-* Give generate scaffold a more descriptive database message. Closes #7316 [jeremymcanally]
+* Give generate scaffold a more descriptive database message. Closes #7316 [Jeremy McAnally]
* Canonicalize RAILS_ROOT by using File.expand_path on Windows, which doesn't have to worry about symlinks, and Pathname#realpath elsewhere, which respects symlinks in relative paths but is incompatible with Windows. #6755 [Jeremy Kemper, trevor]
@@ -474,19 +474,19 @@ databases on localhost. #9753 [Trevor Wennblom]
* Split plugin location and loading out of the initializer and into a new Plugin namespace, which includes Plugin::Locater and Plugin::Loader. The loader class that is used can be customized using the config.plugin_loader option. Those monkey patching the plugin loading subsystem take note, the internals changing here will likely break your modifications. The good news is that it should be substantially easier to hook into the plugin locating and loading process now. [Marcel Molina Jr.]
-* Added assumption that all plugin creators desire to be sharing individuals and release their work under the MIT license [DHH]
+* Added assumption that all plugin creators desire to be sharing individuals and release their work under the MIT license [David Heinemeier Hansson]
* Added source-annotations extractor tasks to rake [Jamis Buck]. This allows you to add FIXME, OPTIMIZE, and TODO comments to your source code that can then be extracted in concert with rake notes (shows all), rake notes:fixme, rake notes:optimize and rake notes:todo.
-* Added fixtures :all to test_helper.rb to assume that most people just want all their fixtures loaded all the time [DHH]
+* Added fixtures :all to test_helper.rb to assume that most people just want all their fixtures loaded all the time [David Heinemeier Hansson]
-* Added config/initializers where all ruby files within it are automatically loaded after the Rails configuration is done, so you don't have to litter the environment.rb file with a ton of mixed stuff [DHH]
+* Added config/initializers where all ruby files within it are automatically loaded after the Rails configuration is done, so you don't have to litter the environment.rb file with a ton of mixed stuff [David Heinemeier Hansson]
* For new apps, generate a random secret for the cookie-based session store. [Jeremy Kemper]
-* Stop swallowing errors during rake test [Koz]
+* Stop swallowing errors during rake test [Michael Koziarski]
-* Update Rails Initializer to use ActionController::Base#view_paths [Rick]
+* Update Rails Initializer to use ActionController::Base#view_paths [Rick Olson]
* Fix gem deprecation warnings, which also means depending on RubyGems 0.9.0+ [Chad Fowler]
@@ -504,12 +504,12 @@ databases on localhost. #9753 [Trevor Wennblom]
* Fixed that webrick would strip leading newlines and hang connection #4156 [psross]
-* Ensure plugins are in the Dependencies.load_once_paths collection by default. [Rick]
+* Ensure plugins are in the Dependencies.load_once_paths collection by default. [Rick Olson]
If you really want your plugins to reload, add this to the very top of init.rb:
Dependencies.load_once_paths.delete(lib_path)
-* Allow config.to_prepare to work, make the dispatcher safe to 're require'. [Koz, Nicholas Seckar]
+* Allow config.to_prepare to work, make the dispatcher safe to 're require'. [Michael Koziarski, Nicholas Seckar]
* Fix scaffold_resource generator so it respects the --pretend argument when creating the routes file. Closes #6852 [fearoffish]
@@ -519,25 +519,25 @@ databases on localhost. #9753 [Trevor Wennblom]
* Don't generate a components directory in new Rails apps. [Jeremy Kemper]
-* Fixed script/process/spawner to work properly with Mongrel including in -r (daemonize mode) [DHH]
+* Fixed script/process/spawner to work properly with Mongrel including in -r (daemonize mode) [David Heinemeier Hansson]
-* Added one-letter aliases for the three default environments to script/console, so script/console p will load the production environment (t for test, d for development) [DHH]
+* Added one-letter aliases for the three default environments to script/console, so script/console p will load the production environment (t for test, d for development) [David Heinemeier Hansson]
-* Fixed that script/server running against Mongrel should tail the proper log regardless of the environment [DHH]
+* Fixed that script/server running against Mongrel should tail the proper log regardless of the environment [David Heinemeier Hansson]
* Update initializer to load Rails::VERSION as soon as possible. Closes #6698. [Nicholas Seckar]
-* Added ActiveRecord::Base.clear_active_connections! in development mode so the database connection is not carried over from request to request. Some databases won't reread the schema if that doesn't happen (I'm looking at you SQLite), so you have to restart the server after each migration (= no fun) [DHH]
+* Added ActiveRecord::Base.clear_active_connections! in development mode so the database connection is not carried over from request to request. Some databases won't reread the schema if that doesn't happen (I'm looking at you SQLite), so you have to restart the server after each migration (= no fun) [David Heinemeier Hansson]
-* Made RAILS_GEM_VERSION work for beta gems too, so specifying 1.1.6 will give you 1.1.6.4520 if available [DHH]
+* Made RAILS_GEM_VERSION work for beta gems too, so specifying 1.1.6 will give you 1.1.6.4520 if available [David Heinemeier Hansson]
* Update to Prototype and script.aculo.us [5579]. [Thomas Fuchs]
-* Made script/server work with -e and -d when using Mongrel [DHH]
+* Made script/server work with -e and -d when using Mongrel [David Heinemeier Hansson]
* Update to Prototype 1.5.0_rc2 [5550] which makes it work in Opera again [Thomas Fuchs]
-* Make sure that exceptions which are thrown outside of the user code try their best to be handeled in ApplicationController#rescue_action [Tobias Luetke]
+* Make sure that exceptions which are thrown outside of the user code try their best to be handeled in ApplicationController#rescue_action [Tobias Lütke]
* Rails::VERSION::STRING should always be available without having to require 'rails/version'. #6244 [fearoffish]
@@ -561,31 +561,31 @@ databases on localhost. #9753 [Trevor Wennblom]
* resource and scaffold_resource generators add a restful route to config/routes.rb [Jeremy Kemper]
-* Revert environment changes for autoload_paths. [Koz]
+* Revert environment changes for autoload_paths. [Michael Koziarski]
* Update to latest Prototype, which doesn't serialize disabled form elements, adds clone() to arrays, empty/non-string Element.update() and adds a fixes excessive error reporting in WebKit beta versions [Thomas Fuchs]
-* Clean up the output of rake stats, de-emphasise components and apis, and remove the indents for tests [Koz]
+* Clean up the output of rake stats, de-emphasise components and apis, and remove the indents for tests [Michael Koziarski]
-* Added option to script/process/spawner of specifying the binding address #5133 [zsombor]
+* Added option to script/process/spawner of specifying the binding address #5133 [Dee Zsombor]
* Update environment.rb comments to include config.autoload_paths. Closes #6478 [caio]
-* Update scaffold to use new form_tag block functionality. Closes #6480. [BobSilva]
+* Update scaffold to use new form_tag block functionality. Closes #6480. [Bob Silva]
* Plugin generator: check for class collisions. #4833 [vinbarnes@gmail.com]
* Mailer generator: handle mailers in modules, set mime_version in unit test. [Jeremy Kemper]
-* Set $KCODE to 'u' by default to enable the multibyte safe String#chars proxy. [Koz]
+* Set $KCODE to 'u' by default to enable the multibyte safe String#chars proxy. [Michael Koziarski]
-* Added config.plugins to control which plugins are loaded #6269 [skaes]. By default, everything in vendor/plugins will be loaded, but if you specify config.plugins, only those will be loaded. Example:
+* Added config.plugins to control which plugins are loaded #6269 [Stefan Kaes]. By default, everything in vendor/plugins will be loaded, but if you specify config.plugins, only those will be loaded. Example:
config.plugins = %w[ routing_navigator simply_helpful ]
* Clean up html on included error pages. [Tim Lucas]
-* Fixed default 404.html and 500.htmls to remove extreme ugliness and include human language [DHH]
+* Fixed default 404.html and 500.htmls to remove extreme ugliness and include human language [David Heinemeier Hansson]
* Update to latest Prototype and script.aculo.us trunk versions [Thomas Fuchs]
@@ -593,17 +593,17 @@ databases on localhost. #9753 [Trevor Wennblom]
* Fixed test:uncommitted on Windows (backslash issue) #4999 [paul@paulbutcher.com]
-* Fixed migration creation to work with namespaced models, so script/generate model Gallery::Image will use create_table :gallery_images #6327 [BobSilva]
+* Fixed migration creation to work with namespaced models, so script/generate model Gallery::Image will use create_table :gallery_images #6327 [Bob Silva]
* Fixed rename_table on SQLite tables with indexes defined #5942 [brandon@opensoul.org]
-* Added default timeout setting of 5 seconds to SQLite3 database.yml configurations [DHH]
+* Added default timeout setting of 5 seconds to SQLite3 database.yml configurations [David Heinemeier Hansson]
-* Added generated attribute options to script/generate model, like the one found in scaffold_resource and resource [DHH]. Examples:
+* Added generated attribute options to script/generate model, like the one found in scaffold_resource and resource [David Heinemeier Hansson]. Examples:
./script/generate model post title:string created_on:date body:text published:boolean
-* Added script/generate resource which works just like scaffold_resource, but creates empty placeholders instead of predefined [DHH]
+* Added script/generate resource which works just like scaffold_resource, but creates empty placeholders instead of predefined [David Heinemeier Hansson]
* script/runner can run files, pass on arguments, and be used as a shebang. #6286 [Tuxie, dlpond]
#!/usr/bin/env /path/to/my/app/script/runner
@@ -612,7 +612,7 @@ databases on localhost. #9753 [Trevor Wennblom]
* Look for rake tasks in plugin subdirs. #6259 [obrie]
-* Added map.connect ':controller/:action/:id.:format' as a default route to config/routes.rb [DHH]
+* Added map.connect ':controller/:action/:id.:format' as a default route to config/routes.rb [David Heinemeier Hansson]
* Updated prototype.js to 1.5.0_rc1 with latest fixes. [Rick Olson]
@@ -624,7 +624,7 @@ databases on localhost. #9753 [Trevor Wennblom]
* script/server creates the tmp/pids directory. #6204 [jonathan]
-* Fix script/console --sandbox for internal transactions changes. #5738 [chris@octopod.info, charles.gerungan@gmail.com]
+* Fix script/console --sandbox for internal transactions changes. #5738 [Chris McGrath, charles.gerungan@gmail.com]
* Remove the uncanny default of adding all app/models/*/ directories to the load path. This change will break application which expect the current behavior. As
documented in initializer.rb, the workaround is:
@@ -637,7 +637,7 @@ References #6031. [Nicholas Seckar]
* Update to Prototype 1.5.0_rc1 [sam]
-* Formally Deprecate the old rake tasks. [Koz]
+* Formally Deprecate the old rake tasks. [Michael Koziarski]
* Thoroughly test the FCGI dispatcher. #5970 [Kevin Clark]
@@ -670,7 +670,7 @@ References #6031. [Nicholas Seckar]
* Fix Dispatcher.reset_application! so that AR subclasses are removed and Observers re-initialized *after* Reloadable classes are removed. Closes #5743. [Rick Olson]
-* Clarify usage of script/plugin source. Closes #5344. [james.adam@gmail.com]
+* Clarify usage of script/plugin source. Closes #5344. [James Adam]
* Add Dispatcher.to_prepare and config.to_prepare to provide a pre-request hook. [Nicholas Seckar]
@@ -678,7 +678,7 @@ References #6031. [Nicholas Seckar]
* Added that you can change the web server port in config/lighttpd.conf from script/server --port/-p #5465 [mats@imediatec.co.uk]
-* script/performance/profiler compatibility with the new ruby-prof, including an option to choose the results printer. #5679 [shugo@ruby-lang.org]
+* script/performance/profiler compatibility with the new ruby-prof, including an option to choose the results printer. #5679 [Shugo Maeda]
* Fixed the failsafe response so it uses either the current recognized controller or ApplicationController. [Rick Olson]
@@ -692,7 +692,7 @@ References #6031. [Nicholas Seckar]
* Ensure the logger is initialized. #5629 [mike@clarkware.com]
-* Added Mongrel-spawning capabilities to script/process/spawner. Mongrel will be the default choice if installed, otherwise FCGI is tried [DHH]. Examples:
+* Added Mongrel-spawning capabilities to script/process/spawner. Mongrel will be the default choice if installed, otherwise FCGI is tried [David Heinemeier Hansson]. Examples:
spawner # starts instances on 8000, 8001, and 8002 using Mongrel if available
spawner fcgi # starts instances on 8000, 8001, and 8002 using FCGI
@@ -735,15 +735,15 @@ References #6031. [Nicholas Seckar]
* Mongrel: script/server works on Win32. #5499 [jeremydurham@gmail.com]
-* Remove opts.on { |options[:option_name] } style hash assignment. Closes #4440. [nicksieger@gmail.com]
+* Remove opts.on { |options[:option_name] } style hash assignment. Closes #4440. [Nick Sieger]
* Mongrel support for script/server. #5475 [jeremydurham@gmail.com]
-* Fix script/plugin so it doesn't barf on invalid URLs [Rick]
+* Fix script/plugin so it doesn't barf on invalid URLs [Rick Olson]
* Fix plugin install bug at dir with space. (closes #5359) [Yoshimasa NIWA]
-* Fix bug with 'script/plugin install' so it reports unknown plugin names correctly. [Rick]
+* Fix bug with 'script/plugin install' so it reports unknown plugin names correctly. [Rick Olson]
* Added uninstall.rb hook to plugin handling, such that plugins have a way of removing assets and other artifacts on removal #5003 [takiuchi@drecom.co.jp]
@@ -753,11 +753,11 @@ References #6031. [Nicholas Seckar]
* Make "script/plugin install" work with svn+ssh URLs. [Sam Stephenson]
-* Added lib/ to the directories that will get application docs generated [DHH]
+* Added lib/ to the directories that will get application docs generated [David Heinemeier Hansson]
-* Add observer generator. Closes #5167. [francois.beausoleil@gmail.com]
+* Add observer generator. Closes #5167. [François Beausoleil]
-* Session migration generator obeys pluralize_table_names. #5145 [james.adam@gmail.com]
+* Session migration generator obeys pluralize_table_names. #5145 [James Adam]
* rake test:recent understands subdirectories. #2925 [jerrett@bravenet.com]
@@ -765,17 +765,17 @@ References #6031. [Nicholas Seckar]
* The app generator sets a session key in application.rb so apps running on the same host may distinguish their cookies. #2967 [rcoder, rails-bug@owl.me.uk]
-* Distinguish the spawners for different processes [DHH]
+* Distinguish the spawners for different processes [David Heinemeier Hansson]
-* Added -n/--process to script/process/spawner name the process pid (default is dispatch) [DHH]
+* Added -n/--process to script/process/spawner name the process pid (default is dispatch) [David Heinemeier Hansson]
* Namespaced OrderedHash so the Rails implementation does not clash with any others. (fixes #4911) [Julian Tarkhanov]
* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
-* Added script/process/inspector to do simple process status information on Rails dispatchers keeping pid files in tmp/pids [DHH]
+* Added script/process/inspector to do simple process status information on Rails dispatchers keeping pid files in tmp/pids [David Heinemeier Hansson]
-* Added pid file usage to script/process/spawner and script/process/reaper along with a directive in default config/lighttpd.conf file to record the pid. They will all save their pid file in tmp/pids [DHH]
+* Added pid file usage to script/process/spawner and script/process/reaper along with a directive in default config/lighttpd.conf file to record the pid. They will all save their pid file in tmp/pids [David Heinemeier Hansson]
*1.2.3* (March 12th, 2007)
@@ -784,14 +784,14 @@ References #6031. [Nicholas Seckar]
* Windows: include MinGW in RUBY_PLATFORM check. #2982 [okkez000@gmail.com, Kaspar Schiess]
-* Stop swallowing errors during rake test [Koz]
+* Stop swallowing errors during rake test [Michael Koziarski]
*1.2.2* (February 5th, 2007)
* Fix gem deprecation warnings, which also means depending on RubyGems 0.9.0+ [Chad Fowler]
-* Require the dispatcher for Rails::Configuration#to_prepare. [Rick]
+* Require the dispatcher for Rails::Configuration#to_prepare. [Rick Olson]
*1.2.1* (January 16th, 2007)
@@ -807,7 +807,7 @@ References #6031. [Nicholas Seckar]
* Fixed that webrick would strip leading newlines and hang connection #4156 [psross]
-* Ensure plugins are in the Dependencies.load_once_paths collection by default. [Rick]
+* Ensure plugins are in the Dependencies.load_once_paths collection by default. [Rick Olson]
If you really want your plugins to reload, add this to the very top of init.rb:
Dependencies.load_once_paths.delete(lib_path)
@@ -820,23 +820,23 @@ References #6031. [Nicholas Seckar]
* Don't generate a components directory in new Rails apps. [Jeremy Kemper]
-* Fixed script/process/spawner to work properly with Mongrel including in -r (daemonize mode) [DHH]
+* Fixed script/process/spawner to work properly with Mongrel including in -r (daemonize mode) [David Heinemeier Hansson]
-* Deprecated the name route "root" as it'll be used as a shortcut for map.connect '' in Rails 2.0 [DHH]
+* Deprecated the name route "root" as it'll be used as a shortcut for map.connect '' in Rails 2.0 [David Heinemeier Hansson]
-* Fixed that script/server running against Mongrel should tail the proper log regardless of the environment [DHH]
+* Fixed that script/server running against Mongrel should tail the proper log regardless of the environment [David Heinemeier Hansson]
* Update initializer to load Rails::VERSION as soon as possible. Closes #6698. [Nicholas Seckar]
-* Added ActiveRecord::Base.clear_active_connections! in development mode so the database connection is not carried over from request to request. Some databases won't reread the schema if that doesn't happen (I'm looking at you SQLite), so you have to restart the server after each migration (= no fun) [DHH]
+* Added ActiveRecord::Base.clear_active_connections! in development mode so the database connection is not carried over from request to request. Some databases won't reread the schema if that doesn't happen (I'm looking at you SQLite), so you have to restart the server after each migration (= no fun) [David Heinemeier Hansson]
-* Made RAILS_GEM_VERSION work for beta gems too, so specifying 1.1.6 will give you 1.1.6.4520 if available [DHH]
+* Made RAILS_GEM_VERSION work for beta gems too, so specifying 1.1.6 will give you 1.1.6.4520 if available [David Heinemeier Hansson]
* Update to Prototype and script.aculo.us [5579]. [Sam Stephenson, Thomas Fuchs]
-* Made script/server work with -e and -d when using Mongrel [DHH]
+* Made script/server work with -e and -d when using Mongrel [David Heinemeier Hansson]
-* Make sure that exceptions which are thrown outside of the user code try their best to be handeled in ApplicationController#rescue_action [Tobias Luetke]
+* Make sure that exceptions which are thrown outside of the user code try their best to be handeled in ApplicationController#rescue_action [Tobias Lütke]
* Rails::VERSION::STRING should always be available without having to require 'rails/version'. #6244 [fearoffish]
@@ -856,29 +856,29 @@ References #6031. [Nicholas Seckar]
* resource and scaffold_resource generators add a restful route to config/routes.rb [Jeremy Kemper]
-* Revert environment changes for autoload_paths. [Koz]
+* Revert environment changes for autoload_paths. [Michael Koziarski]
-* Clean up the output of rake stats, de-emphasise components and apis, and remove the indents for tests [Koz]
+* Clean up the output of rake stats, de-emphasise components and apis, and remove the indents for tests [Michael Koziarski]
-* Added option to script/process/spawner of specifying the binding address #5133 [zsombor]
+* Added option to script/process/spawner of specifying the binding address #5133 [Dee Zsombor]
* Update environment.rb comments to include config.autoload_paths. Closes #6478 [caio]
-* Update scaffold to use new form_tag block functionality. Closes #6480. [BobSilva]
+* Update scaffold to use new form_tag block functionality. Closes #6480. [Bob Silva]
* Plugin generator: check for class collisions. #4833 [vinbarnes@gmail.com]
* Mailer generator: handle mailers in modules, set mime_version in unit test. [Jeremy Kemper]
-* Set $KCODE to 'u' by default to enable the multibyte safe String#chars proxy. [Koz]
+* Set $KCODE to 'u' by default to enable the multibyte safe String#chars proxy. [Michael Koziarski]
-* Added config.plugins to control which plugins are loaded #6269 [skaes]. By default, everything in vendor/plugins will be loaded, but if you specify config.plugins, only those will be loaded. Example:
+* Added config.plugins to control which plugins are loaded #6269 [Stefan Kaes]. By default, everything in vendor/plugins will be loaded, but if you specify config.plugins, only those will be loaded. Example:
config.plugins = %w[ routing_navigator simply_helpful ]
* Clean up html on included error pages. [Tim Lucas]
-* Fixed default 404.html and 500.htmls to remove extreme ugliness and include human language [DHH]
+* Fixed default 404.html and 500.htmls to remove extreme ugliness and include human language [David Heinemeier Hansson]
* Update to latest Prototype and script.aculo.us trunk versions [Thomas Fuchs]
@@ -886,17 +886,17 @@ References #6031. [Nicholas Seckar]
* Fixed test:uncommitted on Windows (backslash issue) #4999 [paul@paulbutcher.com]
-* Fixed migration creation to work with namespaced models, so script/generate model Gallery::Image will use create_table :gallery_images #6327 [BobSilva]
+* Fixed migration creation to work with namespaced models, so script/generate model Gallery::Image will use create_table :gallery_images #6327 [Bob Silva]
* Fixed rename_table on SQLite tables with indexes defined #5942 [brandon@opensoul.org]
-* Added default timeout setting of 5 seconds to SQLite3 database.yml configurations [DHH]
+* Added default timeout setting of 5 seconds to SQLite3 database.yml configurations [David Heinemeier Hansson]
-* Added generated attribute options to script/generate model, like the one found in scaffold_resource and resource [DHH]. Examples:
+* Added generated attribute options to script/generate model, like the one found in scaffold_resource and resource [David Heinemeier Hansson]. Examples:
./script/generate model post title:string created_on:date body:text published:boolean
-* Added script/generate resource which works just like scaffold_resource, but creates empty placeholders instead of predefined [DHH]
+* Added script/generate resource which works just like scaffold_resource, but creates empty placeholders instead of predefined [David Heinemeier Hansson]
* script/runner can run files, pass on arguments, and be used as a shebang. #6286 [Tuxie, dlpond]
#!/usr/bin/env /path/to/my/app/script/runner
@@ -905,13 +905,13 @@ References #6031. [Nicholas Seckar]
* Look for rake tasks in plugin subdirs. #6259 [obrie]
-* Added map.connect ':controller/:action/:id.:format' as a default route to config/routes.rb [DHH]
+* Added map.connect ':controller/:action/:id.:format' as a default route to config/routes.rb [David Heinemeier Hansson]
* session_migration generator adds an index on updated_at. #6207 [grg]
* script/server creates the tmp/pids directory. #6204 [jonathan]
-* Fix script/console --sandbox for internal transactions changes. #5738 [chris@octopod.info, charles.gerungan@gmail.com]
+* Fix script/console --sandbox for internal transactions changes. #5738 [Chris McGrath, charles.gerungan@gmail.com]
* Remove the uncanny default of adding all app/models/*/ directories to the load path. This change will break application which expect the current behavior. As
documented in initializer.rb, the workaround is:
@@ -922,7 +922,7 @@ References #6031. [Nicholas Seckar]
* Update to script.aculo.us 1.6.3 [Thomas Fuchs]
-* Formally Deprecate the old rake tasks. [Koz]
+* Formally Deprecate the old rake tasks. [Michael Koziarski]
* Thoroughly test the FCGI dispatcher. #5970 [Kevin Clark]
@@ -953,7 +953,7 @@ References #6031. [Nicholas Seckar]
* Fix Dispatcher.reset_application! so that AR subclasses are removed and Observers re-initialized *after* Reloadable classes are removed. Closes #5743. [Rick Olson]
-* Clarify usage of script/plugin source. Closes #5344. [james.adam@gmail.com]
+* Clarify usage of script/plugin source. Closes #5344. [James Adam]
* Add Dispatcher.to_prepare and config.to_prepare to provide a pre-request hook. [Nicholas Seckar]
@@ -961,7 +961,7 @@ References #6031. [Nicholas Seckar]
* Added that you can change the web server port in config/lighttpd.conf from script/server --port/-p #5465 [mats@imediatec.co.uk]
-* script/performance/profiler compatibility with the new ruby-prof, including an option to choose the results printer. #5679 [shugo@ruby-lang.org]
+* script/performance/profiler compatibility with the new ruby-prof, including an option to choose the results printer. #5679 [Shugo Maeda]
* Fixed the failsafe response so it uses either the current recognized controller or ApplicationController. [Rick Olson]
@@ -975,7 +975,7 @@ References #6031. [Nicholas Seckar]
* Ensure the logger is initialized. #5629 [mike@clarkware.com]
-* Added Mongrel-spawning capabilities to script/process/spawner. Mongrel will be the default choice if installed, otherwise FCGI is tried [DHH]. Examples:
+* Added Mongrel-spawning capabilities to script/process/spawner. Mongrel will be the default choice if installed, otherwise FCGI is tried [David Heinemeier Hansson]. Examples:
spawner # starts instances on 8000, 8001, and 8002 using Mongrel if available
spawner fcgi # starts instances on 8000, 8001, and 8002 using FCGI
@@ -1018,15 +1018,15 @@ References #6031. [Nicholas Seckar]
* Mongrel: script/server works on Win32. #5499 [jeremydurham@gmail.com]
-* Remove opts.on { |options[:option_name] } style hash assignment. Closes #4440. [nicksieger@gmail.com]
+* Remove opts.on { |options[:option_name] } style hash assignment. Closes #4440. [Nick Sieger]
* Mongrel support for script/server. #5475 [jeremydurham@gmail.com]
-* Fix script/plugin so it doesn't barf on invalid URLs [Rick]
+* Fix script/plugin so it doesn't barf on invalid URLs [Rick Olson]
* Fix plugin install bug at dir with space. (closes #5359) [Yoshimasa NIWA]
-* Fix bug with 'script/plugin install' so it reports unknown plugin names correctly. [Rick]
+* Fix bug with 'script/plugin install' so it reports unknown plugin names correctly. [Rick Olson]
* Added uninstall.rb hook to plugin handling, such that plugins have a way of removing assets and other artifacts on removal #5003 [takiuchi@drecom.co.jp]
@@ -1036,11 +1036,11 @@ References #6031. [Nicholas Seckar]
* Make "script/plugin install" work with svn+ssh URLs. [Sam Stephenson]
-* Added lib/ to the directories that will get application docs generated [DHH]
+* Added lib/ to the directories that will get application docs generated [David Heinemeier Hansson]
-* Add observer generator. Closes #5167. [francois.beausoleil@gmail.com]
+* Add observer generator. Closes #5167. [François Beausoleil]
-* Session migration generator obeys pluralize_table_names. #5145 [james.adam@gmail.com]
+* Session migration generator obeys pluralize_table_names. #5145 [James Adam]
* rake test:recent understands subdirectories. #2925 [jerrett@bravenet.com]
@@ -1048,17 +1048,17 @@ References #6031. [Nicholas Seckar]
* The app generator sets a session key in application.rb so apps running on the same host may distinguish their cookies. #2967 [rcoder, rails-bug@owl.me.uk]
-* Distinguish the spawners for different processes [DHH]
+* Distinguish the spawners for different processes [David Heinemeier Hansson]
-* Added -n/--process to script/process/spawner name the process pid (default is dispatch) [DHH]
+* Added -n/--process to script/process/spawner name the process pid (default is dispatch) [David Heinemeier Hansson]
* Namespaced OrderedHash so the Rails implementation does not clash with any others. (fixes #4911) [Julian Tarkhanov]
* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
-* Added script/process/inspector to do simple process status information on Rails dispatchers keeping pid files in tmp/pids [DHH]
+* Added script/process/inspector to do simple process status information on Rails dispatchers keeping pid files in tmp/pids [David Heinemeier Hansson]
-* Added pid file usage to script/process/spawner and script/process/reaper along with a directive in default config/lighttpd.conf file to record the pid. They will all save their pid file in tmp/pids [DHH]
+* Added pid file usage to script/process/spawner and script/process/reaper along with a directive in default config/lighttpd.conf file to record the pid. They will all save their pid file in tmp/pids [David Heinemeier Hansson]
*1.1.6* (August 10th, 2006)
@@ -1093,9 +1093,9 @@ References #6031. [Nicholas Seckar]
* Change the scaffolding layout to use yield rather than @content_for_layout. [Marcel Molina Jr.]
-* Added rake rails:update:configs to update config/boot.rb from the latest (also included in rake rails:update) [DHH]
+* Added rake rails:update:configs to update config/boot.rb from the latest (also included in rake rails:update) [David Heinemeier Hansson]
-* Fixed that boot.rb would set RAILS_GEM_VERSION twice, not respect an uncommented RAILS_GEM_VERSION line, and not use require_gem [DHH]
+* Fixed that boot.rb would set RAILS_GEM_VERSION twice, not respect an uncommented RAILS_GEM_VERSION line, and not use require_gem [David Heinemeier Hansson]
*1.1.1* (April 6th, 2006)
@@ -1106,7 +1106,7 @@ References #6031. [Nicholas Seckar]
* Fixed that the -r/--ruby path option of the rails command was not being respected #4549 [ryan.raaum@gmail.com]
-* Added that Dispatcher exceptions should not be shown to the user unless a default log has not been configured. Instead show public/500.html [DHH]
+* Added that Dispatcher exceptions should not be shown to the user unless a default log has not been configured. Instead show public/500.html [David Heinemeier Hansson]
* Fixed that rake clone_structure_to_test should quit on pgsql if the dump is unsuccesful #4585 [augustz@augustz.com]
@@ -1114,9 +1114,9 @@ References #6031. [Nicholas Seckar]
* Install alias so Rails::InfoController is accessible at /rails_info. Closes #4546. [Nicholas Seckar]
-* Fixed that spawner should daemonize if running in repeat mode [DHH]
+* Fixed that spawner should daemonize if running in repeat mode [David Heinemeier Hansson]
-* Added TAG option for rake rails:freeze:edge, so you can say rake rails:freeze:edge TAG=rel_1-1-0 to lock to the 1.1.0 release [DHH]
+* Added TAG option for rake rails:freeze:edge, so you can say rake rails:freeze:edge TAG=rel_1-1-0 to lock to the 1.1.0 release [David Heinemeier Hansson]
* Applied Prototype $() performance patches (#4465, #4477) and updated script.aculo.us [Sam Stephenson, Thomas Fuchs]
@@ -1126,13 +1126,13 @@ References #6031. [Nicholas Seckar]
* Fixed rake rails:freeze:gems #4518 [benji@silverinsanity.com]
-* Added -f/--freeze option to rails command for freezing the application to the Rails version it was generated with [DHH]
+* Added -f/--freeze option to rails command for freezing the application to the Rails version it was generated with [David Heinemeier Hansson]
* Added gem binding of apps generated through the rails command to the gems of they were generated with [Nicholas Seckar]
-* Added expiration settings for JavaScript, CSS, HTML, and images to default lighttpd.conf [DHH]
+* Added expiration settings for JavaScript, CSS, HTML, and images to default lighttpd.conf [David Heinemeier Hansson]
-* Added gzip compression for JavaScript, CSS, and HTML to default lighttpd.conf [DHH]
+* Added gzip compression for JavaScript, CSS, and HTML to default lighttpd.conf [David Heinemeier Hansson]
* Avoid passing escapeHTML non-string in Rails' info controller [Nicholas Seckar]
@@ -1191,19 +1191,19 @@ References #6031. [Nicholas Seckar]
* Added tracking of database and framework versions in script/about #4088 [charles.gerungan@gmail.com/Rick Olson]
-* Added public/javascripts/application.js as a sample since it'll automatically be included in javascript_include_tag :defaults [DHH]
+* Added public/javascripts/application.js as a sample since it'll automatically be included in javascript_include_tag :defaults [David Heinemeier Hansson]
-* Added socket cleanup for lighttpd, both before and after [DHH]
+* Added socket cleanup for lighttpd, both before and after [David Heinemeier Hansson]
-* Added automatic creation of tmp/ when running script/server [DHH]
+* Added automatic creation of tmp/ when running script/server [David Heinemeier Hansson]
-* Added silence_stream that'll work on both STDERR or STDOUT or any other stream and deprecated silence_stderr in the process [DHH]
+* Added silence_stream that'll work on both STDERR or STDOUT or any other stream and deprecated silence_stderr in the process [David Heinemeier Hansson]
* Added reload! method to script/console to reload all models and others that include Reloadable without quitting the console #4056 [esad@esse.at]
-* Added that rake rails:freeze:edge will now just export all the contents of the frameworks instead of just lib, so stuff like rails:update:scripts, rails:update:javascripts, and script/server on lighttpd still just works #4047 [DHH]
+* Added that rake rails:freeze:edge will now just export all the contents of the frameworks instead of just lib, so stuff like rails:update:scripts, rails:update:javascripts, and script/server on lighttpd still just works #4047 [David Heinemeier Hansson]
-* Added fix for upload problems with lighttpd from Safari/IE to config/lighttpd.conf #3999 [thijs@fngtps.com]
+* Added fix for upload problems with lighttpd from Safari/IE to config/lighttpd.conf #3999 [Thijs van der Vossen]
* Added test:uncommitted to test changes since last checkin to Subversion #4035 [technomancy@gmail.com]
@@ -1217,9 +1217,9 @@ References #6031. [Nicholas Seckar]
* Add integration test support to app generation and testing [Jamis Buck]
-* Added namespaces to all tasks, so for example load_fixtures is now db:fixtures:load. All the old task names are still valid, they just point to the new namespaced names. "rake -T" will only show the namespaced ones, though [DHH]
+* Added namespaces to all tasks, so for example load_fixtures is now db:fixtures:load. All the old task names are still valid, they just point to the new namespaced names. "rake -T" will only show the namespaced ones, though [David Heinemeier Hansson]
-* CHANGED DEFAULT: ActiveRecord::Base.schema_format is now :ruby by default instead of :sql. This means that we'll assume you want to live in the world of db/schema.rb where the grass is green and the girls are pretty. If your schema contains un-dumpable elements, such as constraints or database-specific column types, you just got an invitation to either 1) patch the dumper to include foreign key support, 2) stop being db specific, or 3) just change the default in config/environment.rb to config.active_record.schema_format = :sql -- we even include an example for that on new Rails skeletons now. Brought to you by the federation of opinionated framework builders! [DHH]
+* CHANGED DEFAULT: ActiveRecord::Base.schema_format is now :ruby by default instead of :sql. This means that we'll assume you want to live in the world of db/schema.rb where the grass is green and the girls are pretty. If your schema contains un-dumpable elements, such as constraints or database-specific column types, you just got an invitation to either 1) patch the dumper to include foreign key support, 2) stop being db specific, or 3) just change the default in config/environment.rb to config.active_record.schema_format = :sql -- we even include an example for that on new Rails skeletons now. Brought to you by the federation of opinionated framework builders! [David Heinemeier Hansson]
* Added -r/--repeat option to script/process/spawner that offers the same loop protection as the spinner did. This deprecates the script/process/spinner, so it's no longer included in the default Rails skeleton, but still available for backwards compatibility #3461 [ror@andreas-s.net]
@@ -1230,17 +1230,17 @@ References #6031. [Nicholas Seckar]
* Added more information to script/plugin's doings to ease debugging #3755 [Rick Olson]
-* Changed the default configuration for lighttpd to use tmp/sockets instead of log/ for the FastCGI sockets [DHH]
+* Changed the default configuration for lighttpd to use tmp/sockets instead of log/ for the FastCGI sockets [David Heinemeier Hansson]
-* Added a default configuration of the FileStore for fragment caching if tmp/cache is available, which makes action/fragment caching ready to use out of the box with no additional configuration [DHH]
+* Added a default configuration of the FileStore for fragment caching if tmp/cache is available, which makes action/fragment caching ready to use out of the box with no additional configuration [David Heinemeier Hansson]
-* Changed the default session configuration to place sessions in tmp/sessions, if that directory is available, instead of /tmp (this essentially means a goodbye to 9/10 White Screen of Death errors and should have web hosting firms around the world cheering) [DHH]
+* Changed the default session configuration to place sessions in tmp/sessions, if that directory is available, instead of /tmp (this essentially means a goodbye to 9/10 White Screen of Death errors and should have web hosting firms around the world cheering) [David Heinemeier Hansson]
-* Added tmp/sessions, tmp/cache, and tmp/sockets as default directories in the Rails skeleton [DHH]
+* Added tmp/sessions, tmp/cache, and tmp/sockets as default directories in the Rails skeleton [David Heinemeier Hansson]
-* Added that script/generate model will now automatically create a migration file for the model created. This can be turned off by calling the generator with --skip-migration [DHH]
+* Added that script/generate model will now automatically create a migration file for the model created. This can be turned off by calling the generator with --skip-migration [David Heinemeier Hansson]
-* Added -d/--database option to the rails command, so you can do "rails --database=sqlite2 myapp" to start a new application preconfigured to use SQLite2 as the database. Removed the configuration examples from SQLite and PostgreSQL from the default MySQL configuration [DHH]
+* Added -d/--database option to the rails command, so you can do "rails --database=sqlite2 myapp" to start a new application preconfigured to use SQLite2 as the database. Removed the configuration examples from SQLite and PostgreSQL from the default MySQL configuration [David Heinemeier Hansson]
* Allow script/server -c /path/to/lighttpd.conf [Jeremy Kemper]
@@ -1248,13 +1248,13 @@ References #6031. [Nicholas Seckar]
* Update script.aculo.us to V1.5.3 [Thomas Fuchs]
-* Added SIGTRAP signal handler to RailsFCGIHandler that'll force the process into a breakpoint after the next request. This breakpoint can then be caught with script/breakpointer and give you access to the Ruby image inside that process. Useful for debugging memory leaks among other things [DHH]
+* Added SIGTRAP signal handler to RailsFCGIHandler that'll force the process into a breakpoint after the next request. This breakpoint can then be caught with script/breakpointer and give you access to the Ruby image inside that process. Useful for debugging memory leaks among other things [David Heinemeier Hansson]
-* Changed default lighttpd.conf to use CWD from lighttpd 1.4.10 that allows the same configuration to be used for both detach and not. Also ensured that auto-repeaping of FCGIs only happens when lighttpd is not detached. [DHH]
+* Changed default lighttpd.conf to use CWD from lighttpd 1.4.10 that allows the same configuration to be used for both detach and not. Also ensured that auto-repeaping of FCGIs only happens when lighttpd is not detached. [David Heinemeier Hansson]
* Added Configuration#after_initialize for registering a block which gets called after the framework is fully initialized. Useful for things like per-environment configuration of plugins. [Michael Koziarski]
-* Added check for RAILS_FRAMEWORK_ROOT constant that allows the Rails framework to be found in a different place than vendor/rails. Should be set in boot.rb. [DHH]
+* Added check for RAILS_FRAMEWORK_ROOT constant that allows the Rails framework to be found in a different place than vendor/rails. Should be set in boot.rb. [David Heinemeier Hansson]
* Fixed that static requests could unlock the mutex guarding dynamic requests in the WEBrick servlet #3433 [tom@craz8.com]
@@ -1272,7 +1272,7 @@ References #6031. [Nicholas Seckar]
* Fix typo in mailer generator USAGE. #3458 [chriztian.steinmeier@gmail.com]
-* Ignore version mismatch between pg_dump and the database server. #3457 [simon.stapleton@gmail.com]
+* Ignore version mismatch between pg_dump and the database server. #3457 [Simon Stapleton]
* Reap FCGI processes after lighttpd exits. [Sam Stephenson]
@@ -1304,11 +1304,11 @@ References #6031. [Nicholas Seckar]
* Have the lighttpd server script report the actual ip to which the server is bound. #2903. [Adam]
-* Add plugin library directories to the load path after the lib directory so that libraries in the lib directory get precedence. #2910. [james.adam@gmail.com]
+* Add plugin library directories to the load path after the lib directory so that libraries in the lib directory get precedence. #2910. [James Adam]
* Make help for the console command more explicit about how to specify the desired environment in which to run the console. #2911. [anonymous]
-* PostgreSQL: the purge_test_database Rake task shouldn't explicitly specify the template0 template when creating a fresh test database. #2964 [dreamer3@gmail.com]
+* PostgreSQL: the purge_test_database Rake task shouldn't explicitly specify the template0 template when creating a fresh test database. #2964 [Dreamer3]
* Introducing the session_migration generator. Creates an add_session_table migration. Allows generator to specify migrations directory. #2958, #2960 [Rick Olson]
@@ -1326,19 +1326,19 @@ References #6031. [Nicholas Seckar]
* Make sure that legacy db tasks also reference :database for SQLite #2830 [kazuhiko@fdiary.net]
-* Pass __FILE__ when evaluating plugins' init.rb. #2817 [james.adam@gmail.com]
+* Pass __FILE__ when evaluating plugins' init.rb. #2817 [James Adam]
* Better svn status matching for generators. #2814 [François Beausoleil <francois.beausoleil@gmail.com>, Blair Zajac <blair@orcaware.com>]
-* Don't reload routes until plugins have been loaded so they have a chance to extend the routing capabilities [DHH]
+* Don't reload routes until plugins have been loaded so they have a chance to extend the routing capabilities [David Heinemeier Hansson]
* Don't detach or fork for script/server tailing [Nicholas Seckar]
-* Changed all script/* to use #!/usr/bin/env ruby instead of hard-coded Ruby path. public/dispatcher.* still uses the hard-coded path for compatibility with web servers that don't have Ruby in path [DHH]
+* Changed all script/* to use #!/usr/bin/env ruby instead of hard-coded Ruby path. public/dispatcher.* still uses the hard-coded path for compatibility with web servers that don't have Ruby in path [David Heinemeier Hansson]
-* Force RAILS_ENV to be "test" when running tests, so that ENV["RAILS_ENV"] = "production" in config/environment.rb doesn't wreck havok [DHH] #2660
+* Force RAILS_ENV to be "test" when running tests, so that ENV["RAILS_ENV"] = "production" in config/environment.rb doesn't wreck havok [David Heinemeier Hansson] #2660
-* Correct versioning in :freeze_gems Rake task. #2778 [jakob@mentalized.net, Jeremy Kemper]
+* Correct versioning in :freeze_gems Rake task. #2778 [Jakob Skjerning, Jeremy Kemper]
* Added an omnipresent RailsInfoController with a properties action that delivers an HTML rendering of Rails::Info (but only when local_request? is true). Added a new default index.html which fetches this with Ajax. [Sam Stephenson]
@@ -1353,7 +1353,7 @@ References #6031. [Nicholas Seckar]
* Use require_library_or_gem 'fcgi' in script/server [Sam Stephenson]
-* Added default lighttpd config in config/lighttpd.conf and added a default runner for lighttpd in script/server (works like script/server, but using lighttpd and FastCGI). It will use lighttpd if available, otherwise WEBrick. You can force either or using 'script/server lighttpd' or 'script/server webrick' [DHH]
+* Added default lighttpd config in config/lighttpd.conf and added a default runner for lighttpd in script/server (works like script/server, but using lighttpd and FastCGI). It will use lighttpd if available, otherwise WEBrick. You can force either or using 'script/server lighttpd' or 'script/server webrick' [David Heinemeier Hansson]
* New configuration option config.plugin_paths which may be a single path like the default 'vendor/plugins' or an array of paths: ['vendor/plugins', 'lib/plugins']. [Jeremy Kemper]
@@ -1379,7 +1379,7 @@ References #6031. [Nicholas Seckar]
* Make fcgi handler respond to TERM signals with an explicit exit [Jamis Buck]
-* Added demonstration of fixture use to the test case generated by the model generator [DHH]
+* Added demonstration of fixture use to the test case generated by the model generator [David Heinemeier Hansson]
* If specified, pass PostgreSQL client character encoding to createdb. #2703 [Kazuhiko <kazuhiko@fdiary.net>]
@@ -1399,7 +1399,7 @@ References #6031. [Nicholas Seckar]
* The PostgreSQL :db_structure_dump Rake task limits its dump to the schema search path in database.yml. [Anatol Pomozov <anatol.pomozov@gmail.com>]
-* Add task to generate rdoc for all installed plugins. [Marcel Molina]
+* Add task to generate rdoc for all installed plugins. [Marcel Molina Jr.]
* Update script.aculo.us to V1.5_rc4 [Thomas Fuchs]
@@ -1407,15 +1407,15 @@ References #6031. [Nicholas Seckar]
* Migrations may be destroyed: script/destroy migration foo. #2635 [Charles M. Gerungan <charles.gerungan@gmail.com>, Jamis Buck, Jeremy Kemper]
-* Added that plugins can carry generators and that generator stub files can be created along with new plugins using script/generate plugin <name> --with-generator [DHH]
+* Added that plugins can carry generators and that generator stub files can be created along with new plugins using script/generate plugin <name> --with-generator [David Heinemeier Hansson]
-* Removed app/apis as a default empty dir since its automatically created when using script/generate web_service [DHH]
+* Removed app/apis as a default empty dir since its automatically created when using script/generate web_service [David Heinemeier Hansson]
* Added script/plugin to manage plugins (install, remove, list, etc) [Ryan Tomayko]
-* Added test_plugins task: Run the plugin tests in vendor/plugins/**/test (or specify with PLUGIN=name) [DHH]
+* Added test_plugins task: Run the plugin tests in vendor/plugins/**/test (or specify with PLUGIN=name) [David Heinemeier Hansson]
-* Added plugin generator to create a stub structure for a new plugin in vendor/plugins (see "script/generate plugin" for help) [DHH]
+* Added plugin generator to create a stub structure for a new plugin in vendor/plugins (see "script/generate plugin" for help) [David Heinemeier Hansson]
* Fixed scaffold generator when started with only 1 parameter #2609 [self@mattmower.com]
@@ -1446,7 +1446,7 @@ References #6031. [Nicholas Seckar]
*0.14.0 (RC1)* (October 16th, 2005)
-* Moved generator folder from RAILS_ROOT/generators to RAILS_ROOT/lib/generators [Tobias Luetke]
+* Moved generator folder from RAILS_ROOT/generators to RAILS_ROOT/lib/generators [Tobias Lütke]
* Fix rake dev and related commands [Nicholas Seckar]
@@ -1457,7 +1457,7 @@ References #6031. [Nicholas Seckar]
* Added Rails framework freezing tasks: freeze_gems (freeze to current gems), freeze_edge (freeze to Rails SVN trunk), unfreeze_rails (float with newest gems on system)
-* Added update_javascripts task which will fetch all the latest js files from your current rails install. Use after updating rails. [Tobias Luetke]
+* Added update_javascripts task which will fetch all the latest js files from your current rails install. Use after updating rails. [Tobias Lütke]
* Added cleaning of RAILS_ROOT to useless elements such as '../non-dot-dot/'. Provides cleaner backtraces and error messages. [Nicholas Seckar]
@@ -1483,7 +1483,7 @@ References #6031. [Nicholas Seckar]
* Make the migration generator only check files ending in *.rb when calculating the next file name #2317 [Chad Fowler]
-* Added prevention of duplicate migrations from the generator #2240 [fbeausoleil@ftml.net]
+* Added prevention of duplicate migrations from the generator #2240 [François Beausoleil]
* Add db_schema_dump and db_schema_import rake tasks to work with the new ActiveRecord::SchemaDumper (for dumping a schema to and reading a schema from a ruby file).
@@ -1499,7 +1499,7 @@ References #6031. [Nicholas Seckar]
* Added -m/--mime-types option to the WEBrick server, so you can specify a Apache-style mime.types file to load #2059 [ask@develooper.com]
-* Added -c/--svn option to the generator that'll add new files and remove destroyed files using svn add/revert/remove as appropriate #2064 [kevin.clark@gmail.com]
+* Added -c/--svn option to the generator that'll add new files and remove destroyed files using svn add/revert/remove as appropriate #2064 [Kevin Clark]
* Added -c/--charset option to WEBrick server, so you can specify a default charset (which without changes is UTF-8) #2084 [wejn@box.cz]
@@ -1531,9 +1531,9 @@ References #6031. [Nicholas Seckar]
* Added convenience controls for FCGI processes (especially when managed remotely): spinner, spawner, and reaper. They reside in script/process. More details can be had by calling them with -h/--help.
-* Added load_fixtures task to the Rakefile, which will load all the fixtures into the database for the current environment #1791 [Marcel Molina]
+* Added load_fixtures task to the Rakefile, which will load all the fixtures into the database for the current environment #1791 [Marcel Molina Jr.]
-* Added an empty robots.txt to public/, so that web servers asking for it won't trigger a dynamic call, like favicon.ico #1738 [michael@schubert]
+* Added an empty robots.txt to public/, so that web servers asking for it won't trigger a dynamic call, like favicon.ico #1738 [Michael Schubert]
* Dropped the 'immediate close-down' of FCGI processes since it didn't work consistently and produced bad responses when it didn't. So now a TERM ensures exit after the next request (just as if the process is handling a request when it receives the signal). This means that you'll have to 'nudge' all FCGI processes with a request in order to ensure that they have all reloaded. This can be done by something like ./script/process/repear --nudge 'http://www.myapp.com' --instances 10, which will load the myapp site 10 times (and thus hit all of the 10 FCGI processes once, enough to shut down).
@@ -1544,13 +1544,13 @@ References #6031. [Nicholas Seckar]
* Fixed that each request with the WEBrick adapter would open a new database connection #1685 [Sam Stephenson]
-* Added support for SQL Server in the database rake tasks #1652 [ken.barker@gmail.com] Note: osql and scptxfr may need to be installed on your development environment. This involves getting the .exes and a .rll (scptxfr) from a production SQL Server (not developer level SQL Server). Add their location to your Environment PATH and you are all set.
+* Added support for SQL Server in the database rake tasks #1652 [Ken Barker] Note: osql and scptxfr may need to be installed on your development environment. This involves getting the .exes and a .rll (scptxfr) from a production SQL Server (not developer level SQL Server). Add their location to your Environment PATH and you are all set.
* Added a VERSION parameter to the migrate task that allows you to do "rake migrate VERSION=34" to migrate to the 34th version traveling up or down depending on the current version
-* Extend Ruby version check to include RUBY_RELEASE_DATE >= '2005-12-25', the final Ruby 1.8.2 release #1674 [court3nay@gmail.com]
+* Extend Ruby version check to include RUBY_RELEASE_DATE >= '2005-12-25', the final Ruby 1.8.2 release #1674 [court3nay]
-* Improved documentation for environment config files #1625 [court3nay@gmail.com]
+* Improved documentation for environment config files #1625 [court3nay]
*0.13.0* (6 July, 2005)
@@ -1561,19 +1561,19 @@ References #6031. [Nicholas Seckar]
* Added "migrate" as rake task to execute all the pending migrations from db/migrate
-* Fixed that model generator would make fixtures plural, even if ActiveRecord::Base.pluralize_table_names was false #1185 [Marcel Molina]
+* Fixed that model generator would make fixtures plural, even if ActiveRecord::Base.pluralize_table_names was false #1185 [Marcel Molina Jr.]
* Added a DOCTYPE of HTML transitional to the HTML files generated by Rails #1124 [Michael Koziarski]
* SIGTERM also gracefully exits dispatch.fcgi. Ignore SIGUSR1 on Windows.
-* Add the option to manually manage garbage collection in the FastCGI dispatcher. Set the number of requests between GC runs in your public/dispatch.fcgi [skaes@web.de]
+* Add the option to manually manage garbage collection in the FastCGI dispatcher. Set the number of requests between GC runs in your public/dispatch.fcgi [Stefan Kaes]
* Allow dynamic application reloading for dispatch.fcgi processes by sending a SIGHUP. If the process is currently handling a request, the request will be allowed to complete first. This allows production fcgi's to be reloaded without having to restart them.
* RailsFCGIHandler (dispatch.fcgi) no longer tries to explicitly flush $stdout (CgiProcess#out always calls flush)
-* Fixed rakefile actions against PostgreSQL when the password is all numeric #1462 [michael@schubert.cx]
+* Fixed rakefile actions against PostgreSQL when the password is all numeric #1462 [Michael Schubert]
* ActionMailer::Base subclasses are reloaded with the other rails components #1262
@@ -1587,7 +1587,7 @@ References #6031. [Nicholas Seckar]
* Fixed Webrick to escape + characters in URL's the same way that lighttpd and apache do #1397 [Nicholas Seckar]
-* Added -e/--environment option to script/runner #1408 [fbeausoleil@ftml.net]
+* Added -e/--environment option to script/runner #1408 [François Beausoleil]
* Modernize the scaffold generator to use the simplified render and test methods and to change style from @params["id"] to params[:id]. #1367
@@ -1628,7 +1628,7 @@ References #6031. [Nicholas Seckar]
* Added lazy typing for generate, such that ./script/generate cn == ./script/generate controller and the likes #1051 [k@v2studio.com]
-* Fixed that ownership is brought over in pg_dump during tests for PostgreSQL #1060 [pburleson@gmail.com]
+* Fixed that ownership is brought over in pg_dump during tests for PostgreSQL #1060 [pburleson]
* Upgraded to Active Record 1.10.0, Action Pack 1.8.0, Action Mailer 0.9.0, Action Web Service 0.7.0, Active Support 1.0.4
@@ -1648,11 +1648,11 @@ References #6031. [Nicholas Seckar]
* Added additional error handling to the FastCGI dispatcher to catch even errors taking down the entire process
-* Improved the generated scaffold code a lot to take advantage of recent Rails developments #882 [Tobias Luetke]
+* Improved the generated scaffold code a lot to take advantage of recent Rails developments #882 [Tobias Lütke]
* Combined the script/environment.rb used for gems and regular files version. If vendor/rails/* has all the frameworks, then files version is used, otherwise gems #878 [Nicholas Seckar]
-* Changed .htaccess to allow dispatch.* to be called from a sub-directory as part of the push with Action Pack to make Rails work on non-vhost setups #826 [Nicholas Seckar/Tobias Luetke]
+* Changed .htaccess to allow dispatch.* to be called from a sub-directory as part of the push with Action Pack to make Rails work on non-vhost setups #826 [Nicholas Seckar/Tobias Lütke]
* Added script/runner which can be used to run code inside the environment by eval'ing the first parameter. Examples:
@@ -1676,9 +1676,9 @@ References #6031. [Nicholas Seckar]
*0.10.1* (7th March, 2005)
-* Fixed rake stats to ignore editor backup files like model.rb~ #791 [skanthak]
+* Fixed rake stats to ignore editor backup files like model.rb~ #791 [Sebastian Kanthak]
-* Added exception shallowing if the DRb server can't be started (not worth making a fuss about to distract new users) #779 [Tobias Luetke]
+* Added exception shallowing if the DRb server can't be started (not worth making a fuss about to distract new users) #779 [Tobias Lütke]
* Added an empty favicon.ico file to the public directory of new applications (so the logs are not spammed by its absence)
@@ -1711,7 +1711,7 @@ References #6031. [Nicholas Seckar]
*0.10.0* (24th February, 2005)
-* Changed default IP binding for WEBrick from 127.0.0.1 to 0.0.0.0 so that the server is accessible both locally and remotely #696 [Marcel]
+* Changed default IP binding for WEBrick from 127.0.0.1 to 0.0.0.0 so that the server is accessible both locally and remotely #696 [Marcel Molina Jr.]
* Fixed that script/server -d was broken so daemon mode couldn't be used #687 [Nicholas Seckar]
@@ -1778,7 +1778,7 @@ References #6031. [Nicholas Seckar]
*0.9.4.1* (January 18th, 2005)
-* Added 5-second timeout to WordNet alternatives on creating reserved-word models #501 [Marcel Molina]
+* Added 5-second timeout to WordNet alternatives on creating reserved-word models #501 [Marcel Molina Jr.]
* Fixed binding of caller #496 [Alexey]
@@ -1787,22 +1787,22 @@ References #6031. [Nicholas Seckar]
*0.9.4* (January 17th, 2005)
-* Added that ApplicationController will catch a ControllerNotFound exception if someone attempts to access a url pointing to an unexisting controller [Tobias Luetke]
+* Added that ApplicationController will catch a ControllerNotFound exception if someone attempts to access a url pointing to an unexisting controller [Tobias Lütke]
* Flipped code-to-test ratio around to be more readable #468 [Scott Baron]
* Fixed log file permissions to be 666 instead of 777 (so they're not executable) #471 [Lucas Carlson]
-* Fixed that auto reloading would some times not work or would reload the models twice #475 [Tobias Luetke]
+* Fixed that auto reloading would some times not work or would reload the models twice #475 [Tobias Lütke]
* Added rewrite rules to deal with caching to public/.htaccess
* Added the option to specify a controller name to "generate scaffold" and made the default controller name the plural form of the model.
* Added that rake clone_structure_to_test, db_structure_dump, and purge_test_database tasks now pick up the source database to use from
- RAILS_ENV instead of just forcing development #424 [Tobias Luetke]
+ RAILS_ENV instead of just forcing development #424 [Tobias Lütke]
-* Fixed script/console to work with Windows (that requires the use of irb.bat) #418 [octopod]
+* Fixed script/console to work with Windows (that requires the use of irb.bat) #418 [Chris McGrath]
* Fixed WEBrick servlet slowdown over time by restricting the load path reloading to mod_ruby
diff --git a/railties/doc/guides/html/activerecord_validations_callbacks.html b/railties/doc/guides/html/activerecord_validations_callbacks.html
index cb381e7191..039e3d1891 100644
--- a/railties/doc/guides/html/activerecord_validations_callbacks.html
+++ b/railties/doc/guides/html/activerecord_validations_callbacks.html
@@ -231,7 +231,7 @@ ul#navMain {
<li><a href="#_the_tt_validates_length_of_tt_helper">The <tt>validates_length_of</tt> helper</a></li>
- <li><a href="#_the_tt_validates_numericallity_of_tt_helper">The <tt>validates_numericallity_of</tt> helper</a></li>
+ <li><a href="#_the_tt_validates_numericality_of_tt_helper">The <tt>validates_numericality_of</tt> helper</a></li>
<li><a href="#_the_tt_validates_presence_of_tt_helper">The <tt>validates_presence_of</tt> helper</a></li>
@@ -338,7 +338,7 @@ Create your own custom validation methods
</li>
<li>
<p>
-Work with the error messages generated by the validation proccess
+Work with the error messages generated by the validation process
</p>
</li>
<li>
@@ -405,7 +405,7 @@ http://www.gnu.org/software/src-highlite -->
&gt;&gt; p.new_record?
=&gt; false</tt></pre>
</div></div>
-<div class="para"><p>Saving new records means sending an SQL insert operation to the database, while saving existing records (by calling either <tt>save</tt> or <tt>update_attributes</tt>) will result in a SQL update operation. Active Record will use this facts to perform validations upon your objects, avoiding then to be recorded to the database if their inner state is invalid in some way. You can specify validations that will be beformed every time a object is saved, just when you're creating a new record or when you're updating an existing one.</p></div>
+<div class="para"><p>Saving new records means sending an SQL insert operation to the database, while saving existing records (by calling either <tt>save</tt> or <tt>update_attributes</tt>) will result in a SQL update operation. Active Record will use these facts to perform validations upon your objects, avoiding then to be recorded to the database if their inner state is invalid in some way. You can specify validations that will be beformed every time a object is saved, just when you're creating a new record or when you're updating an existing one.</p></div>
<div class="admonitionblock">
<table><tr>
<td class="icon">
@@ -524,7 +524,8 @@ by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> MovieFile <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
- validates_exclusion_of <span style="color: #990000">:</span>format<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">in</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #990000">%</span>w<span style="color: #990000">(</span>mov avi<span style="color: #990000">),</span> <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Extension %s is not allowed"</span>
+ validates_exclusion_of <span style="color: #990000">:</span>format<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">in</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #990000">%</span>w<span style="color: #990000">(</span>mov avi<span style="color: #990000">),</span>
+ <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Extension %s is not allowed"</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
<div class="para"><p>The <tt>validates_exclusion_of</tt> helper has an option <tt>:in</tt> that receives the set of values that will not be accepted for the validated attributes. The <tt>:in</tt> option has an alias called <tt>:within</tt> that you can use for the same purpose, if you'd like to. In the previous example we used the <tt>:message</tt> option to show how we can personalize it with the current attribute's value, through the <tt>%s</tt> format mask.</p></div>
@@ -537,7 +538,8 @@ by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Product <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
- validates_format_of <span style="color: #990000">:</span>description<span style="color: #990000">,</span> <span style="color: #990000">:</span>with <span style="color: #990000">=&gt;</span> <span style="color: #FF6600">/^[a-zA-Z]+$/</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Only letters allowed"</span>
+ validates_format_of <span style="color: #990000">:</span>description<span style="color: #990000">,</span> <span style="color: #990000">:</span>with <span style="color: #990000">=&gt;</span> <span style="color: #FF6600">/^[a-zA-Z]+$/</span><span style="color: #990000">,</span>
+ <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Only letters allowed"</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
<div class="para"><p>The default error message for <tt>validates_format_of</tt> is "<em>is invalid</em>".</p></div>
@@ -549,7 +551,8 @@ by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Coffee <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
- validates_inclusion_of <span style="color: #990000">:</span>size<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">in</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #990000">%</span>w<span style="color: #990000">(</span>small medium large<span style="color: #990000">),</span> <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"%s is not a valid size"</span>
+ validates_inclusion_of <span style="color: #990000">:</span>size<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">in</span></span> <span style="color: #990000">=&gt;</span> <span style="color: #990000">%</span>w<span style="color: #990000">(</span>small medium large<span style="color: #990000">),</span>
+ <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"%s is not a valid size"</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
<div class="para"><p>The <tt>validates_inclusion_of</tt> helper has an option <tt>:in</tt> that receives the set of values that will be accepted. The <tt>:in</tt> option has an alias called <tt>:within</tt> that you can use for the same purpose, if you'd like to. In the previous example we used the <tt>:message</tt> option to show how we can personalize it with the current attribute's value, through the <tt>%s</tt> format mask.</p></div>
@@ -602,7 +605,7 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
<div class="para"><p>This helper has an alias called <tt>validates_size_of</tt>, it's the same helper with a different name. You can use it if you'd like to.</p></div>
-<h3 id="_the_tt_validates_numericallity_of_tt_helper">3.9. The <tt>validates_numericallity_of</tt> helper</h3>
+<h3 id="_the_tt_validates_numericality_of_tt_helper">3.9. The <tt>validates_numericality_of</tt> helper</h3>
<div class="para"><p>This helper validates that your attributes have only numeric values. By default, it will match an optional sign followed by a integral or floating point number. Using the <tt>:integer_only</tt> option set to true, you can specify that only integral numbers are allowed.</p></div>
<div class="para"><p>If you use <tt>:integer_only</tt> set to <tt>true</tt>, then it will use the <tt><span>/\A[+\-]?\d+\Z/</span></tt> regular expression to validate the attribute's value. Otherwise, it will try to convert the value using <tt>Kernel.Float</tt>.</p></div>
<div class="listingblock">
@@ -611,11 +614,11 @@ by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Player <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
- validates_numericallity_of <span style="color: #990000">:</span>points
- validates_numericallity_of <span style="color: #990000">:</span>games_played<span style="color: #990000">,</span> <span style="color: #990000">:</span>integer_only <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
+ validates_numericality_of <span style="color: #990000">:</span>points
+ validates_numericality_of <span style="color: #990000">:</span>games_played<span style="color: #990000">,</span> <span style="color: #990000">:</span>integer_only <span style="color: #990000">=&gt;</span> <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
-<div class="para"><p>The default error message for <tt>validates_numericallity_of</tt> is "<em>is not a number</em>".</p></div>
+<div class="para"><p>The default error message for <tt>validates_numericality_of</tt> is "<em>is not a number</em>".</p></div>
<h3 id="_the_tt_validates_presence_of_tt_helper">3.10. The <tt>validates_presence_of</tt> helper</h3>
<div class="para"><p>This helper validates that the attributes are not empty. It uses the <tt>blank?</tt> method to check if the value is either <tt>nil</tt> or an empty string (if the string has only spaces, it will still be considered empty).</p></div>
<div class="listingblock">
@@ -673,7 +676,8 @@ by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Holiday <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
- validates_uniqueness_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>scope <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>year<span style="color: #990000">,</span> <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Should happen once per year"</span>
+ validates_uniqueness_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>scope <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>year<span style="color: #990000">,</span>
+ <span style="color: #990000">:</span>message <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"Should happen once per year"</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
<div class="para"><p>There is also a <tt>:case_sensitive</tt> option that you can use to define if the uniqueness contraint will be case sensitive or not. This option defaults to true.</p></div>
@@ -692,7 +696,7 @@ http://www.gnu.org/software/src-highlite -->
<div class="sectionbody">
<div class="para"><p>There are some common options that all the validation helpers can use. Here they are, except for the <tt>:if</tt> and <tt>:unless</tt> options, which we'll cover right at the next topic.</p></div>
<h3 id="_the_tt_allow_nil_tt_option">4.1. The <tt>:allow_nil</tt> option</h3>
-<div class="para"><p>You may use the <tt>:allow_nil</tt> option everytime you just want to trigger a validation if the value being validated is not <tt>nil</tt>. You may be asking yourself if it makes any sense to use <tt>:allow_nil</tt> and <tt>validates_presence_of</tt> together. Well, it does. Remember, validation will be skipped only for <tt>nil</tt> attributes, but empty strings are not considered <tt>nil</tt>.</p></div>
+<div class="para"><p>You may use the <tt>:allow_nil</tt> option everytime you want to trigger a validation only if the value being validated is not <tt>nil</tt>. You may be asking yourself if it makes any sense to use <tt>:allow_nil</tt> and <tt>validates_presence_of</tt> together. Well, it does. Remember, validation will be skipped only for <tt>nil</tt> attributes, but empty strings are not considered <tt>nil</tt>.</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@@ -713,9 +717,14 @@ by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Person <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
- validates_uniqueness_of <span style="color: #990000">:</span>email<span style="color: #990000">,</span> <span style="color: #990000">:</span>on <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>create <span style="font-style: italic"><span style="color: #9A1900"># =&gt; it will be possible to update email with a duplicated value</span></span>
- validates_numericallity_of <span style="color: #990000">:</span>age<span style="color: #990000">,</span> <span style="color: #990000">:</span>on <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>update <span style="font-style: italic"><span style="color: #9A1900"># =&gt; it will be possible to create the record with a 'non-numerical age'</span></span>
- validates_presence_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>on <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>save <span style="font-style: italic"><span style="color: #9A1900"># =&gt; that's the default</span></span>
+ <span style="font-style: italic"><span style="color: #9A1900"># =&gt; it will be possible to update email with a duplicated value</span></span>
+ validates_uniqueness_of <span style="color: #990000">:</span>email<span style="color: #990000">,</span> <span style="color: #990000">:</span>on <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>create
+
+ <span style="font-style: italic"><span style="color: #9A1900"># =&gt; it will be possible to create the record with a 'non-numerical age'</span></span>
+ validates_numericality_of <span style="color: #990000">:</span>age<span style="color: #990000">,</span> <span style="color: #990000">:</span>on <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>update
+
+ <span style="font-style: italic"><span style="color: #9A1900"># =&gt; the default</span></span>
+ validates_presence_of <span style="color: #990000">:</span>name<span style="color: #990000">,</span> <span style="color: #990000">:</span>on <span style="color: #990000">=&gt;</span> <span style="color: #990000">:</span>save
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
</div>
@@ -756,7 +765,8 @@ by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Account <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
- validates_confirmation_of <span style="color: #990000">:</span>password<span style="color: #990000">,</span> <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">unless</span></span> <span style="color: #990000">=&gt;</span> Proc<span style="color: #990000">.</span>new <span style="color: #FF0000">{</span> <span style="color: #990000">|</span>a<span style="color: #990000">|</span> a<span style="color: #990000">.</span>password<span style="color: #990000">.</span>blank? <span style="color: #FF0000">}</span>
+ validates_confirmation_of <span style="color: #990000">:</span>password<span style="color: #990000">,</span>
+ <span style="color: #990000">:</span><span style="font-weight: bold"><span style="color: #0000FF">unless</span></span> <span style="color: #990000">=&gt;</span> Proc<span style="color: #990000">.</span>new <span style="color: #FF0000">{</span> <span style="color: #990000">|</span>a<span style="color: #990000">|</span> a<span style="color: #990000">.</span>password<span style="color: #990000">.</span>blank? <span style="color: #FF0000">}</span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
</div>
@@ -770,7 +780,8 @@ http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Invoice <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> validate_on_create
- errors<span style="color: #990000">.</span>add<span style="color: #990000">(:</span>expiration_date<span style="color: #990000">,</span> <span style="color: #FF0000">"can't be in the past"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #990000">!</span>expiration_date<span style="color: #990000">.</span>blank? <span style="font-weight: bold"><span style="color: #0000FF">and</span></span> expiration_date <span style="color: #990000">&lt;</span> Date<span style="color: #990000">.</span>today
+ errors<span style="color: #990000">.</span>add<span style="color: #990000">(:</span>expiration_date<span style="color: #990000">,</span> <span style="color: #FF0000">"can't be in the past"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">if</span></span>
+ <span style="color: #990000">!</span>expiration_date<span style="color: #990000">.</span>blank? <span style="font-weight: bold"><span style="color: #0000FF">and</span></span> expiration_date <span style="color: #990000">&lt;</span> Date<span style="color: #990000">.</span>today
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
@@ -781,14 +792,17 @@ by Lorenzo Bettini
http://www.lorenzobettini.it
http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Invoice <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
- validate <span style="color: #990000">:</span>expiration_date_cannot_be_in_the_past<span style="color: #990000">,</span> <span style="color: #990000">:</span>discount_cannot_be_more_than_total_value
+ validate <span style="color: #990000">:</span>expiration_date_cannot_be_in_the_past<span style="color: #990000">,</span>
+ <span style="color: #990000">:</span>discount_cannot_be_more_than_total_value
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> expiration_date_cannot_be_in_the_past
- errors<span style="color: #990000">.</span>add<span style="color: #990000">(:</span>expiration_date<span style="color: #990000">,</span> <span style="color: #FF0000">"can't be in the past"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">if</span></span> <span style="color: #990000">!</span>expiration_date<span style="color: #990000">.</span>blank? <span style="font-weight: bold"><span style="color: #0000FF">and</span></span> expiration_date <span style="color: #990000">&lt;</span> Date<span style="color: #990000">.</span>today
+ errors<span style="color: #990000">.</span>add<span style="color: #990000">(:</span>expiration_date<span style="color: #990000">,</span> <span style="color: #FF0000">"can't be in the past"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">if</span></span>
+ <span style="color: #990000">!</span>expiration_date<span style="color: #990000">.</span>blank? <span style="font-weight: bold"><span style="color: #0000FF">and</span></span> expiration_date <span style="color: #990000">&lt;</span> Date<span style="color: #990000">.</span>today
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">def</span></span> discount_cannot_be_greater_than_total_value
- errors<span style="color: #990000">.</span>add<span style="color: #990000">(:</span>discount<span style="color: #990000">,</span> <span style="color: #FF0000">"can't be greater than total value"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">unless</span></span> discount <span style="color: #990000">&lt;=</span> total_value
+ errors<span style="color: #990000">.</span>add<span style="color: #990000">(:</span>discount<span style="color: #990000">,</span> <span style="color: #FF0000">"can't be greater than total value"</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">unless</span></span>
+ discount <span style="color: #990000">&lt;=</span> total_value
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
@@ -874,16 +888,18 @@ person<span style="color: #990000">.</span>errors<span style="color: #990000">.<
person <span style="color: #990000">=</span> Person<span style="color: #990000">.</span>new<span style="color: #990000">(:</span>name <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"JD"</span><span style="color: #990000">)</span>
person<span style="color: #990000">.</span>valid? <span style="font-style: italic"><span style="color: #9A1900"># =&gt; false</span></span>
-person<span style="color: #990000">.</span>errors<span style="color: #990000">.</span>on<span style="color: #990000">(:</span>name<span style="color: #990000">)</span> <span style="font-style: italic"><span style="color: #9A1900"># =&gt; "is too short (minimum is 3 characters)"</span></span>
+person<span style="color: #990000">.</span>errors<span style="color: #990000">.</span>on<span style="color: #990000">(:</span>name<span style="color: #990000">)</span>
+<span style="font-style: italic"><span style="color: #9A1900"># =&gt; "is too short (minimum is 3 characters)"</span></span>
person <span style="color: #990000">=</span> Person<span style="color: #990000">.</span>new
person<span style="color: #990000">.</span>valid? <span style="font-style: italic"><span style="color: #9A1900"># =&gt; false</span></span>
-person<span style="color: #990000">.</span>errors<span style="color: #990000">.</span>on<span style="color: #990000">(:</span>name<span style="color: #990000">)</span> <span style="font-style: italic"><span style="color: #9A1900"># =&gt; ["can't be blank", "is too short (minimum is 3 characters)"]</span></span>
+person<span style="color: #990000">.</span>errors<span style="color: #990000">.</span>on<span style="color: #990000">(:</span>name<span style="color: #990000">)</span>
+<span style="font-style: italic"><span style="color: #9A1900"># =&gt; ["can't be blank", "is too short (minimum is 3 characters)"]</span></span>
</tt></pre></div></div>
<div class="ilist"><ul>
<li>
<p>
-<tt>clear</tt> is used when you intentionally wants to clear all the messages in the <tt>errors</tt> collection.
+<tt>clear</tt> is used when you intentionally want to clear all the messages in the <tt>errors</tt> collection. However, calling <tt>errors.clear</tt> upon an invalid object won't make it valid: the <tt>errors</tt> collection will now be empty, but the next time you call <tt>valid?</tt> or any method that tries to save this object to the database, the validations will run. If any of them fails, the <tt>errors</tt> collection will get filled again.
</p>
</li>
</ul></div>
@@ -898,10 +914,15 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
person <span style="color: #990000">=</span> Person<span style="color: #990000">.</span>new
-puts person<span style="color: #990000">.</span>valid? <span style="font-style: italic"><span style="color: #9A1900"># =&gt; false</span></span>
-person<span style="color: #990000">.</span>errors<span style="color: #990000">.</span>on<span style="color: #990000">(:</span>name<span style="color: #990000">)</span> <span style="font-style: italic"><span style="color: #9A1900"># =&gt; ["can't be blank", "is too short (minimum is 3 characters)"]</span></span>
+person<span style="color: #990000">.</span>valid? <span style="font-style: italic"><span style="color: #9A1900"># =&gt; false</span></span>
+person<span style="color: #990000">.</span>errors<span style="color: #990000">.</span>on<span style="color: #990000">(:</span>name<span style="color: #990000">)</span>
+<span style="font-style: italic"><span style="color: #9A1900"># =&gt; ["can't be blank", "is too short (minimum is 3 characters)"]</span></span>
+
person<span style="color: #990000">.</span>errors<span style="color: #990000">.</span>clear
-person<span style="color: #990000">.</span>errors <span style="font-style: italic"><span style="color: #9A1900"># =&gt; nil</span></span>
+person<span style="color: #990000">.</span>errors<span style="color: #990000">.</span>empty? <span style="font-style: italic"><span style="color: #9A1900"># =&gt; true</span></span>
+p<span style="color: #990000">.</span>save <span style="font-style: italic"><span style="color: #9A1900"># =&gt; false</span></span>
+p<span style="color: #990000">.</span>errors<span style="color: #990000">.</span>on<span style="color: #990000">(:</span>name<span style="color: #990000">)</span>
+<span style="font-style: italic"><span style="color: #9A1900"># =&gt; ["can't be blank", "is too short (minimum is 3 characters)"]</span></span>
</tt></pre></div></div>
</div>
<h2 id="_callbacks">8. Callbacks</h2>
@@ -910,7 +931,7 @@ person<span style="color: #990000">.</span>errors <span style="font-style: itali
<h3 id="_callbacks_registration">8.1. Callbacks registration</h3>
<div class="para"><p>In order to use the available callbacks, you need to registrate them. There are two ways of doing that.</p></div>
<h3 id="_registering_callbacks_by_overriding_the_callback_methods">8.2. Registering callbacks by overriding the callback methods</h3>
-<div class="para"><p>You can specify the callback method direcly, by overriding it. Let's see how it works using the <tt>before_validation</tt> callback, which will surprisingly run right before any validation is done.</p></div>
+<div class="para"><p>You can specify the callback method directly, by overriding it. Let's see how it works using the <tt>before_validation</tt> callback, which will surprisingly run right before any validation is done.</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@@ -1094,7 +1115,7 @@ Readability, since your callback declarations will live at the beggining of your
</div>
<h2 id="_halting_execution">10. Halting Execution</h2>
<div class="sectionbody">
-<div class="para"><p>As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks and the database operation to be executed. However, if at any moment one of the callback methods returns a boolean <tt>false</tt> (not <tt>nil</tt>) value, this execution chain will be halted and the desired operation will not complete: your model will not get persisted in the database, or your records will not get deleted and so on.</p></div>
+<div class="para"><p>As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks and the database operation to be executed. However, if at any moment one of the <tt>before_create</tt>, <tt>before_save</tt>, <tt>before_update</tt> or <tt>before_destroy</tt> callback methods returns a boolean <tt>false</tt> (not <tt>nil</tt>) value, this execution chain will be halted and the desired operation will not complete: your model will not get persisted in the database, or your records will not get deleted and so on.</p></div>
</div>
<h2 id="_callback_classes">11. Callback classes</h2>
<div class="sectionbody">
@@ -1174,7 +1195,7 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
<h3 id="_registering_observers">12.1. Registering observers</h3>
-<div class="para"><p>If you payed attention, you may be wondering where Active Record Observers are referenced in our applications, so they get instantiate and begin to interact with our models. For observers to work we need to register then in our application's <strong>config/environment.rb</strong> file. In this file there is a commented out line where we can define the observers that our application should load at start-up.</p></div>
+<div class="para"><p>If you payed attention, you may be wondering where Active Record Observers are referenced in our applications, so they get instantiate and begin to interact with our models. For observers to work we need to register them somewhere. The usual place to do that is in our application's <strong>config/environment.rb</strong> file. In this file there is a commented out line where we can define the observers that our application should load at start-up.</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
@@ -1183,6 +1204,8 @@ http://www.gnu.org/software/src-highlite -->
<pre><tt><span style="font-style: italic"><span style="color: #9A1900"># Activate observers that should always be running</span></span>
config<span style="color: #990000">.</span>active_record<span style="color: #990000">.</span>observers <span style="color: #990000">=</span> <span style="color: #990000">:</span>registration_observer<span style="color: #990000">,</span> <span style="color: #990000">:</span>auditor
</tt></pre></div></div>
+<div class="para"><p>You can uncomment the line with <tt>config.active_record.observers</tt> and change the symbols for the name of the observers that should be registered.</p></div>
+<div class="para"><p>It's also possible to register callbacks in any of the files living at <strong>config/environments/</strong>, if you want an observer to work only in a specific environment. There is not a <tt>config.active_record.observers</tt> line at any of those files, but you can simply add it.</p></div>
<h3 id="_where_to_put_the_observers_source_files">12.2. Where to put the observers' source files</h3>
<div class="para"><p>By convention, you should always save your observers' source files inside <strong>app/models</strong>.</p></div>
</div>
diff --git a/railties/doc/guides/html/finders.html b/railties/doc/guides/html/finders.html
index c2c1db99e3..603f488cc9 100644
--- a/railties/doc/guides/html/finders.html
+++ b/railties/doc/guides/html/finders.html
@@ -609,6 +609,7 @@ http://www.gnu.org/software/src-highlite -->
<h2 id="_selecting_certain_fields">6. Selecting Certain Fields</h2>
<div class="sectionbody">
<div class="para"><p>To select certain fields, you can use the select option like this: <tt>Client.first(:select &#8658; "viewable_by, locked")</tt>. This select option does not use an array of fields, but rather requires you to type SQL-like code. The above code will execute <tt>SELECT viewable_by, locked FROM clients LIMIT 0,1</tt> on your database.</p></div>
+<div class="para"><p>You can also call SQL functions within the select option. For example, if you would like to only grab a single record per unique value in a certain field by using the <tt>DISTINCT</tt> function you can do it like this: <tt>Client.all(:select &#8658; "DISTINCT(name)")</tt>.</p></div>
</div>
<h2 id="_limit_amp_offset">7. Limit &amp; Offset</h2>
<div class="sectionbody">
@@ -1051,6 +1052,11 @@ http://www.gnu.org/software/src-highlite -->
<div class="ilist"><ul>
<li>
<p>
+December 1 2008: Added using an SQL function example to Selecting Certain Fields section as per <a href="http://rails.lighthouseapp.com/projects/16213/tickets/36-adding-an-example-for-using-distinct-to-ar-finders">this ticket</a>
+</p>
+</li>
+<li>
+<p>
November 23 2008: Added documentation for <tt>find_by_last</tt> and <tt>find_by_bang!</tt>
</p>
</li>
diff --git a/railties/doc/guides/html/getting_started_with_rails.html b/railties/doc/guides/html/getting_started_with_rails.html
index 4fc92f8ad7..ac16a79ac1 100644
--- a/railties/doc/guides/html/getting_started_with_rails.html
+++ b/railties/doc/guides/html/getting_started_with_rails.html
@@ -1723,7 +1723,7 @@ http://www.gnu.org/software/src-highlite -->
<td class="icon">
<img src="./images/icons/tip.png" alt="Tip" />
</td>
-<td class="content">For more information on routing, see the <a href="../routing_outside_in">Rails Routing from the Outside In</a> guide.</td>
+<td class="content">For more information on routing, see the <a href="../routing_outside_in.html">Rails Routing from the Outside In</a> guide.</td>
</tr></table>
</div>
<h3 id="_generating_a_controller">8.4. Generating a Controller</h3>
diff --git a/railties/doc/guides/html/layouts_and_rendering.html b/railties/doc/guides/html/layouts_and_rendering.html
index 7138c45853..30c114ef82 100644
--- a/railties/doc/guides/html/layouts_and_rendering.html
+++ b/railties/doc/guides/html/layouts_and_rendering.html
@@ -642,7 +642,7 @@ end</tt></pre>
<div class="para"><p>With those declarations, the <tt>inventory</tt> layout would be used only for the <tt>index</tt> method, the <tt>product</tt> layout would be used for everything else except the <tt>rss</tt> method, and the <tt>rss</tt> method will have its layout determined by the automatic layout rules.</p></div>
<h5 id="_layout_inheritance">Layout Inheritance</h5>
<div class="para"><p>Layouts are shared downwards in the hierarchy, and more specific layouts always override more general ones. For example:</p></div>
-<div class="para"><p><tt>application.rb</tt>:</p></div>
+<div class="para"><p><tt>application_controller.rb</tt>:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
by Lorenzo Bettini
diff --git a/railties/doc/guides/html/testing_rails_applications.html b/railties/doc/guides/html/testing_rails_applications.html
index b8a99767ee..a94c81dc5f 100644
--- a/railties/doc/guides/html/testing_rails_applications.html
+++ b/railties/doc/guides/html/testing_rails_applications.html
@@ -219,48 +219,9 @@ ul#navMain {
<li><a href="#_preparing_you_application_for_testing">Preparing you Application for Testing</a></li>
- <li><a href="#_running_tests">Running Tests</a></li>
-
- <li><a href="#_what_to_include_in_your_unit_tests">What to Include in Your Unit Tests</a></li>
-
- <li><a href="#_assertions_available">Assertions Available</a></li>
-
- <li><a href="#_rails_specific_assertions">Rails Specific Assertions</a></li>
-
- </ul>
- </li>
- <li>
- <a href="#_functional_tests_for_your_controllers">Functional Tests for Your Controllers</a>
- <ul>
-
- <li><a href="#_what_to_include_in_your_functional_tests">What to include in your Functional Tests</a></li>
-
- <li><a href="#_available_request_types_for_functional_tests">Available Request Types for Functional Tests</a></li>
-
- <li><a href="#_the_4_hashes_of_the_apocalypse">The 4 Hashes of the Apocalypse</a></li>
-
- <li><a href="#_instance_variables_available">Instance Variables Available</a></li>
-
- <li><a href="#_a_fuller_functional_test_example">A Fuller Functional Test Example</a></li>
-
- <li><a href="#_testing_views">Testing Views</a></li>
-
- </ul>
- </li>
- <li>
- <a href="#_integration_testing">Integration Testing</a>
- <ul>
-
- <li><a href="#_helpers_available_for_integration_tests">Helpers Available for Integration tests</a></li>
-
- <li><a href="#_integration_testing_examples">Integration Testing Examples</a></li>
-
</ul>
</li>
<li>
- <a href="#_rake_tasks_for_running_your_tests">Rake Tasks for Running your Tests</a>
- </li>
- <li>
<a href="#_brief_note_about_test_unit">Brief Note About Test::Unit</a>
</li>
<li>
@@ -585,994 +546,595 @@ $ rake db<span style="color: #990000">:</span><span style="font-weight: bold"><s
<td class="content"><tt>db:test:prepare</tt> will fail with an error if db/schema.rb doesn't exists.</td>
</tr></table>
</div>
-<h4 id="_rake_tasks_for_preparing_you_application_for_testing">3.1.1. Rake Tasks for Preparing you Application for Testing ==</h4>
-<div class="para"><p>--------------------------------`----------------------------------------------------
-Tasks Description</p></div>
+<h4 id="_rake_tasks_for_preparing_your_application_for_testing">3.1.1. Rake Tasks for Preparing your Application for Testing</h4>
<div class="listingblock">
<div class="content">
-<pre><tt>+rake db:test:clone+ Recreate the test database from the current environment's database schema
-+rake db:test:clone_structure+ Recreate the test databases from the development structure
-+rake db:test:load+ Recreate the test database from the current +schema.rb+
-+rake db:test:prepare+ Check for pending migrations and load the test schema
-+rake db:test:purge+ Empty the test database.</tt></pre>
+<pre><tt>Tasks Description</tt></pre>
</div></div>
-<div class="admonitionblock">
-<table><tr>
-<td class="icon">
-<img src="./images/icons/tip.png" alt="Tip" />
-</td>
-<td class="content">You can see all these rake tasks and their descriptions by running <tt>rake &#8212;tasks &#8212;describe</tt></td>
-</tr></table>
-</div>
-<h3 id="_running_tests">3.2. Running Tests</h3>
-<div class="para"><p>Running a test is as simple as invoking the file containing the test cases through Ruby:</p></div>
+<div class="para"><p><tt>rake db:test:clone</tt> Recreate the test database from the current environment's database schema
+<tt>rake db:test:clone_structure</tt> Recreate the test databases from the development structure
+<tt>rake db:test:load</tt> Recreate the test database from the current <tt>schema.rb</tt>
+<tt>rake db:test:prepare</tt> Check for pending migrations and load the test schema
+<tt>rake db:test:purge</tt> Empty the test database.</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt>$ cd <span style="font-weight: bold"><span style="color: #0000FF">test</span></span>
-$ ruby unit/post_test<span style="color: #990000">.</span>rb
+<div class="content">
+<pre><tt>
+TIP: You can see all these rake tasks and their descriptions by running +rake \-\-tasks \-\-describe+
-Loaded suite unit/post_test
-Started
-<span style="color: #990000">.</span>
-Finished <span style="font-weight: bold"><span style="color: #0000FF">in</span></span> <span style="color: #993399">0.023513</span> seconds<span style="color: #990000">.</span>
+=== Running Tests ===
-<span style="color: #993399">1</span> tests<span style="color: #990000">,</span> <span style="color: #993399">1</span> assertions<span style="color: #990000">,</span> <span style="color: #993399">0</span> failures<span style="color: #990000">,</span> <span style="color: #993399">0</span> errors
-</tt></pre></div></div>
-<div class="para"><p>This will run all the test methods from the test case.</p></div>
-<div class="para"><p>You can also run a particular test method from the test case by using the <tt>-n</tt> switch with the <tt>test method name</tt>.</p></div>
+Running a test is as simple as invoking the file containing the test cases through Ruby:
+
+[source, shell]</tt></pre>
+</div></div>
+<div class="para"><p>$ cd test
+$ ruby unit/post_test.rb</p></div>
+<div class="para"><p>Loaded suite unit/post_test
+Started
+.
+Finished in 0.023513 seconds.</p></div>
+<div class="para"><p>1 tests, 1 assertions, 0 failures, 0 errors</p></div>
<div class="listingblock">
<div class="content">
-<pre><tt>$ ruby unit/post_test.rb -n test_truth
+<pre><tt>
+This will run all the test methods from the test case.
-Loaded suite unit/post_test
+You can also run a particular test method from the test case by using the +-n+ switch with the +test method name+.</tt></pre>
+</div></div>
+<div class="para"><p>$ ruby unit/post_test.rb -n test_truth</p></div>
+<div class="para"><p>Loaded suite unit/post_test
Started
.
-Finished in 0.023513 seconds.
+Finished in 0.023513 seconds.</p></div>
+<div class="para"><p>1 tests, 1 assertions, 0 failures, 0 errors</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>
+The +.+ (dot) above indicates a passing test. When a test fails you see an +F+; when a test throws an error you see an +E+ in its place. The last line of the output is the summary.
-1 tests, 1 assertions, 0 failures, 0 errors</tt></pre>
+To see how a test failure is reported, you can add a failing test to the +post_test.rb+ test case.
+
+[source,ruby]</tt></pre>
</div></div>
-<div class="para"><p>The <tt>.</tt> (dot) above indicates a passing test. When a test fails you see an <tt>F</tt>; when a test throws an error you see an <tt>E</tt> in its place. The last line of the output is the summary.</p></div>
-<div class="para"><p>To see how a test failure is reported, you can add a failing test to the <tt>post_test.rb</tt> test case.</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_not_save_post_without_title
- post <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>new
- assert <span style="color: #990000">!</span>post<span style="color: #990000">.</span>save
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-</tt></pre></div></div>
-<div class="para"><p>Let us run this newly added test.</p></div>
+<div class="para"><p>def test_should_not_save_post_without_title
+ post = Post.new
+ assert !post.save
+end</p></div>
<div class="listingblock">
<div class="content">
-<pre><tt>$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
+<pre><tt>
+Let us run this newly added test.</tt></pre>
+</div></div>
+<div class="para"><p>$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
Loaded suite unit/post_test
Started
F
-Finished in 0.197094 seconds.
-
- 1) Failure:
+Finished in 0.197094 seconds.</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt> 1) Failure:
test_should_not_save_post_without_title(PostTest)
[unit/post_test.rb:11:in `test_should_not_save_post_without_title'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run']:
-&lt;false&gt; is not true.
-
-1 tests, 1 assertions, 1 failures, 0 errors</tt></pre>
+&lt;false&gt; is not true.</tt></pre>
</div></div>
-<div class="para"><p>In the output, <tt>F</tt> denotes a failure. You can see the corresponding trace shown under <tt>1)</tt> along with the name of the failing test. The next few lines contain the stack trace followed by a message which mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable every assertion provides an optional message parameter, as shown here:</p></div>
+<div class="para"><p>1 tests, 1 assertions, 1 failures, 0 errors</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_not_save_post_without_title
- post <span style="color: #990000">=</span> Post<span style="color: #990000">.</span>new
- assert <span style="color: #990000">!</span>post<span style="color: #990000">.</span>save<span style="color: #990000">,</span> <span style="color: #FF0000">"Saved the post without a title"</span>
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-</tt></pre></div></div>
-<div class="para"><p>Running this test shows the friendlier assertion message:</p></div>
+<div class="content">
+<pre><tt>
+In the output, +F+ denotes a failure. You can see the corresponding trace shown under +1)+ along with the name of the failing test. The next few lines contain the stack trace followed by a message which mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable every assertion provides an optional message parameter, as shown here:
+
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>def test_should_not_save_post_without_title
+ post = Post.new
+ assert !post.save, "Saved the post without a title"
+end</p></div>
<div class="listingblock">
<div class="content">
-<pre><tt>$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
+<pre><tt>
+Running this test shows the friendlier assertion message:</tt></pre>
+</div></div>
+<div class="para"><p>$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
Loaded suite unit/post_test
Started
F
-Finished in 0.198093 seconds.
-
- 1) Failure:
+Finished in 0.198093 seconds.</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt> 1) Failure:
test_should_not_save_post_without_title(PostTest)
[unit/post_test.rb:11:in `test_should_not_save_post_without_title'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run']:
Saved the post without a title.
-&lt;false&gt; is not true.
-
-1 tests, 1 assertions, 1 failures, 0 errors</tt></pre>
+&lt;false&gt; is not true.</tt></pre>
</div></div>
-<div class="para"><p>Now to get this test to pass we can add a model level validation for the <em>title</em> field.</p></div>
+<div class="para"><p>1 tests, 1 assertions, 1 failures, 0 errors</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">class</span></span> Post <span style="color: #990000">&lt;</span> ActiveRecord<span style="color: #990000">::</span>Base
- validates_presence_of <span style="color: #990000">:</span>title
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-</tt></pre></div></div>
-<div class="para"><p>Now the test should pass. Let us verify by running the test again:</p></div>
+<div class="content">
+<pre><tt>
+Now to get this test to pass we can add a model level validation for the _title_ field.
+
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>class Post &lt; ActiveRecord::Base
+ validates_presence_of :title
+end</p></div>
<div class="listingblock">
<div class="content">
-<pre><tt>$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
+<pre><tt>
+Now the test should pass. Let us verify by running the test again:</tt></pre>
+</div></div>
+<div class="para"><p>$ ruby unit/post_test.rb -n test_should_not_save_post_without_title
Loaded suite unit/post_test
Started
.
-Finished in 0.193608 seconds.
+Finished in 0.193608 seconds.</p></div>
+<div class="para"><p>1 tests, 1 assertions, 0 failures, 0 errors</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>
+Now if you noticed we first wrote a test which fails for a desired functionality, then we wrote some code which adds the functionality and finally we ensured that our test passes. This approach to software development is referred to as _Test-Driven Development_ (TDD).
-1 tests, 1 assertions, 0 failures, 0 errors</tt></pre>
+TIP: Many Rails developers practice _Test-Driven Development_ (TDD). This is an excellent way to build up a test suite that exercises every part of your application. TDD is beyond the scope of this guide, but one place to start is with link:http://andrzejonsoftware.blogspot.com/2007/05/15-tdd-steps-to-create-rails.html[15 TDD steps to create a Rails application].
+
+To see how an error gets reported, here's a test containing an error:
+
+[source,ruby]</tt></pre>
</div></div>
-<div class="para"><p>Now if you noticed we first wrote a test which fails for a desired functionality, then we wrote some code which adds the functionality and finally we ensured that our test passes. This approach to software development is referred to as <em>Test-Driven Development</em> (TDD).</p></div>
-<div class="admonitionblock">
-<table><tr>
-<td class="icon">
-<img src="./images/icons/tip.png" alt="Tip" />
-</td>
-<td class="content">Many Rails developers practice <em>Test-Driven Development</em> (TDD). This is an excellent way to build up a test suite that exercises every part of your application. TDD is beyond the scope of this guide, but one place to start is with <a href="http://andrzejonsoftware.blogspot.com/2007/05/15-tdd-steps-to-create-rails.html">15 TDD steps to create a Rails application</a>.</td>
-</tr></table>
-</div>
-<div class="para"><p>To see how an error gets reported, here's a test containing an error:</p></div>
-<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_report_error
- <span style="font-style: italic"><span style="color: #9A1900"># some_undefined_variable is not defined elsewhere in the test case</span></span>
+<div class="para"><p>def test_should_report_error
+ # some_undefined_variable is not defined elsewhere in the test case
some_undefined_variable
- assert <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-</tt></pre></div></div>
-<div class="para"><p>Now you can see even more output in the console from running the tests:</p></div>
+ assert true
+end</p></div>
<div class="listingblock">
<div class="content">
-<pre><tt>$ ruby unit/post_test.rb -n test_should_report_error
+<pre><tt>
+Now you can see even more output in the console from running the tests:</tt></pre>
+</div></div>
+<div class="para"><p>$ ruby unit/post_test.rb -n test_should_report_error
Loaded suite unit/post_test
Started
E
-Finished in 0.195757 seconds.
-
- 1) Error:
+Finished in 0.195757 seconds.</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt> 1) Error:
test_should_report_error(PostTest):
NameError: undefined local variable or method `some_undefined_variable' for #&lt;PostTest:0x2cc9de8&gt;
/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.1.1/lib/action_controller/test_process.rb:467:in `method_missing'
unit/post_test.rb:16:in `test_should_report_error'
/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `__send__'
- /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run'
-
-1 tests, 0 assertions, 0 failures, 1 errors</tt></pre>
+ /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.1/lib/active_support/testing/setup_and_teardown.rb:33:in `run'</tt></pre>
</div></div>
-<div class="para"><p>Notice the <em>E</em> in the output. It denotes a test with error.</p></div>
-<div class="admonitionblock">
-<table><tr>
-<td class="icon">
-<img src="./images/icons/note.png" alt="Note" />
-</td>
-<td class="content">The execution of each test method stops as soon as any error or a assertion failure is encountered, and the test suite continues with the next method. All test methods are executed in alphabetical order.</td>
-</tr></table>
-</div>
-<h3 id="_what_to_include_in_your_unit_tests">3.3. What to Include in Your Unit Tests</h3>
-<div class="para"><p>Ideally you would like to include a test for everything which could possibly break. It's a good practice to have at least one test for each of your validations and at least one test for every method in your model.</p></div>
-<h3 id="_assertions_available">3.4. Assertions Available</h3>
-<div class="para"><p>By now you've caught a glimpse of some of the assertions that are available. Assertions are the worker bees of testing. They are the ones that actually perform the checks to ensure that things are going as planned.</p></div>
-<div class="para"><p>There are a bunch of different types of assertions you can use. Here's the complete list of assertions that ship with <tt>test/unit</tt>, the testing library used by Rails. The <tt>[msg]</tt> parameter is an optional string message you can specify to make your test failure messages clearer. It's not required.</p></div>
-<div class="tableblock">
-<table rules="all"
-frame="hsides"
-cellspacing="0" cellpadding="4">
-<col width="754" />
-<col width="834" />
-<thead>
- <tr>
- <th align="left">
- Assertion
- </th>
- <th align="left">
- Purpose
- </th>
- </tr>
-</thead>
-<tbody valign="top">
- <tr>
- <td align="left">
- <tt>assert( boolean, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that the object/expression is true.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_equal( obj1, obj2, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that <tt>obj1 == obj2</tt> is true.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_not_equal( obj1, obj2, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that <tt>obj1 == obj2</tt> is false.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_same( obj1, obj2, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that <tt>obj1.equal?(obj2)</tt> is true.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_not_same( obj1, obj2, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that <tt>obj1.equal?(obj2)</tt> is false.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_nil( obj, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that <tt>obj.nil?</tt> is true.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_not_nil( obj, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that <tt>obj.nil?</tt> is false.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_match( regexp, string, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that a string matches the regular expression.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_no_match( regexp, string, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that a string doesn't matches the regular expression.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_in_delta( expecting, actual, delta, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that the numbers <tt>expecting</tt> and <tt>actual</tt> are within <tt>delta</tt> of each other.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_throws( symbol, [msg] ) { block }</tt>
- </td>
- <td align="left">
- Ensures that the given block throws the symbol.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_raises( exception1, exception2, &#8230; ) { block }</tt>
- </td>
- <td align="left">
- Ensures that the given block raises one of the given exceptions.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_nothing_raised( exception1, exception2, &#8230; ) { block }</tt>
- </td>
- <td align="left">
- Ensures that the given block doesn't raise one of the given exceptions.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_instance_of( class, obj, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that <tt>obj</tt> is of the <tt>class</tt> type.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_kind_of( class, obj, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that <tt>obj</tt> is or descends from <tt>class</tt>.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_respond_to( obj, symbol, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that <tt>obj</tt> has a method called <tt>symbol</tt>.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_operator( obj1, operator, obj2, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that <tt>obj1.operator(obj2)</tt> is true.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_send( array, [msg] )</tt>
- </td>
- <td align="left">
- Ensures that executing the method listed in <tt>array[1]</tt> on the object in <tt>array[0]</tt> with the parameters of <tt>array[2 and up]</tt> is true. This one is weird eh?
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>flunk( [msg] )</tt>
- </td>
- <td align="left">
- Ensures failure. This is useful to explicitly mark a test that isn't finished yet.
- </td>
- </tr>
-</tbody>
-</table>
-</div>
-<div class="para"><p>Because of the modular nature of the testing framework, it is possible to create your own assertions. In fact, that's exactly what Rails does. It includes some specialized assertions to make your life easier.</p></div>
-<div class="admonitionblock">
-<table><tr>
-<td class="icon">
-<img src="./images/icons/note.png" alt="Note" />
-</td>
-<td class="content">Creating your own assertions is an advanced topic that we won't cover in this tutorial.</td>
-</tr></table>
-</div>
-<h3 id="_rails_specific_assertions">3.5. Rails Specific Assertions</h3>
-<div class="para"><p>Rails adds some custom assertions of its own to the <tt>test/unit</tt> framework:</p></div>
-<div class="tableblock">
-<table rules="all"
-frame="hsides"
-cellspacing="0" cellpadding="4">
-<col width="948" />
-<col width="640" />
-<thead>
- <tr>
- <th align="left">
- Assertion
- </th>
- <th align="left">
- Purpose
- </th>
- </tr>
-</thead>
-<tbody valign="top">
- <tr>
- <td align="left">
- <tt>assert_valid(record)</tt>
- </td>
- <td align="left">
- Ensures that the passed record is valid by Active Record standards and returns any error messages if it is not.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_difference(expressions, difference = 1, message = nil) {|| &#8230;}</tt>
- </td>
- <td align="left">
- Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_no_difference(expressions, message = nil, &amp;block)</tt>
- </td>
- <td align="left">
- Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_recognizes(expected_options, path, extras={}, message=nil)</tt>
- </td>
- <td align="left">
- Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)</tt>
- </td>
- <td align="left">
- Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_response(type, message = nil)</tt>
- </td>
- <td align="left">
- Asserts that the response comes with a specific status code. You can specify <tt>:success</tt> to indicate 200, <tt>:redirect</tt> to indicate 300-399, <tt>:missing</tt> to indicate 404, or <tt>:error</tt> to match the 500-599 range
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_redirected_to(options = {}, message=nil)</tt>
- </td>
- <td align="left">
- Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that <tt>assert_redirected_to(:controller &#8658; "weblog")</tt> will also match the redirection of <tt>redirect_to(:controller &#8658; "weblog", :action &#8658; "show")</tt> and so on.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_template(expected = nil, message=nil)</tt>
- </td>
- <td align="left">
- Asserts that the request was rendered with the appropriate template file.
- </td>
- </tr>
-</tbody>
-</table>
-</div>
-<div class="para"><p>You'll see the usage of some of these assertions in the next chapter.</p></div>
-</div>
-<h2 id="_functional_tests_for_your_controllers">4. Functional Tests for Your Controllers</h2>
-<div class="sectionbody">
-<div class="para"><p>In Rails, testing the various actions of a single controller is called writing functional tests for that controller. Controllers handle the incoming web requests to your application and eventually respond with a rendered view.</p></div>
-<h3 id="_what_to_include_in_your_functional_tests">4.1. What to include in your Functional Tests</h3>
-<div class="para"><p>You should test for things such as:</p></div>
-<div class="ilist"><ul>
-<li>
-<p>
-was the web request successful?
-</p>
-</li>
-<li>
-<p>
-was the user redirected to the right page?
-</p>
-</li>
-<li>
-<p>
-was the user successfully authenticated?
-</p>
-</li>
-<li>
-<p>
-was the correct object stored in the response template?
-</p>
-</li>
-<li>
-<p>
-was the appropriate message displayed to the user in the view
-</p>
-</li>
-</ul></div>
-<div class="para"><p>Now that we have used Rails scaffold generator for our <tt>Post</tt> resource, it has already created the controller code and functional tests. You can take look at the file <tt>posts_controller_test.rb</tt> in the <tt>test/functional</tt> directory.</p></div>
-<div class="para"><p>Let me take you through one such test, <tt>test_should_get_index</tt> from the file <tt>posts_controller_test.rb</tt>.</p></div>
+<div class="para"><p>1 tests, 0 assertions, 0 failures, 1 errors</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_get_index
- get <span style="color: #990000">:</span>index
- assert_response <span style="color: #990000">:</span>success
- assert_not_nil assigns<span style="color: #990000">(:</span>posts<span style="color: #990000">)</span>
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-</tt></pre></div></div>
-<div class="para"><p>In the <tt>test_should_get_index</tt> test, Rails simulates a request on the action called index, making sure the request was successful and also ensuring that it assigns a valid <tt>posts</tt> instance variable.</p></div>
-<div class="para"><p>The <tt>get</tt> method kicks off the web request and populates the results into the response. It accepts 4 arguments:</p></div>
-<div class="ilist"><ul>
-<li>
-<p>
-The action of the controller you are requesting. This can be in the form of a string or a symbol.
-</p>
-</li>
-<li>
-<p>
-An optional hash of request parameters to pass into the action (eg. query string parameters or post variables).
-</p>
-</li>
-<li>
-<p>
-An optional hash of session variables to pass along with the request.
-</p>
-</li>
-<li>
-<p>
-An optional hash of flash values.
-</p>
-</li>
-</ul></div>
-<div class="para"><p>Example: Calling the <tt>:show</tt> action, passing an <tt>id</tt> of 12 as the <tt>params</tt> and setting a <tt>user_id</tt> of 5 in the session:</p></div>
+<div class="content">
+<pre><tt>
+Notice the 'E' in the output. It denotes a test with error.
+
+NOTE: The execution of each test method stops as soon as any error or a assertion failure is encountered, and the test suite continues with the next method. All test methods are executed in alphabetical order.
+
+=== What to Include in Your Unit Tests ===
+
+Ideally you would like to include a test for everything which could possibly break. It's a good practice to have at least one test for each of your validations and at least one test for every method in your model.
+
+=== Assertions Available ===
+
+By now you've caught a glimpse of some of the assertions that are available. Assertions are the worker bees of testing. They are the ones that actually perform the checks to ensure that things are going as planned.
+
+There are a bunch of different types of assertions you can use. Here's the complete list of assertions that ship with +test/unit+, the testing library used by Rails. The +[msg]+ parameter is an optional string message you can specify to make your test failure messages clearer. It's not required.
+
+[grid="all"]
+`-----------------------------------------------------------------`------------------------------------------------------------------------
+Assertion Purpose</tt></pre>
+</div></div>
+<div class="para"><p><tt>assert( boolean, [msg] )</tt> Ensures that the object/expression is true.
+<tt>assert_equal( obj1, obj2, [msg] )</tt> Ensures that <tt>obj1 == obj2</tt> is true.
+<tt>assert_not_equal( obj1, obj2, [msg] )</tt> Ensures that <tt>obj1 == obj2</tt> is false.
+<tt>assert_same( obj1, obj2, [msg] )</tt> Ensures that <tt>obj1.equal?(obj2)</tt> is true.
+<tt>assert_not_same( obj1, obj2, [msg] )</tt> Ensures that <tt>obj1.equal?(obj2)</tt> is false.
+<tt>assert_nil( obj, [msg] )</tt> Ensures that <tt>obj.nil?</tt> is true.
+<tt>assert_not_nil( obj, [msg] )</tt> Ensures that <tt>obj.nil?</tt> is false.
+<tt>assert_match( regexp, string, [msg] )</tt> Ensures that a string matches the regular expression.
+<tt>assert_no_match( regexp, string, [msg] )</tt> Ensures that a string doesn't matches the regular expression.
+<tt>assert_in_delta( expecting, actual, delta, [msg] )</tt> Ensures that the numbers <tt>expecting</tt> and <tt>actual</tt> are within <tt>delta</tt> of each other.
+<tt>assert_throws( symbol, [msg] ) { block }</tt> Ensures that the given block throws the symbol.
+<tt>assert_raises( exception1, exception2, &#8230; ) { block }</tt> Ensures that the given block raises one of the given exceptions.
+<tt>assert_nothing_raised( exception1, exception2, &#8230; ) { block }</tt> Ensures that the given block doesn't raise one of the given exceptions.
+<tt>assert_instance_of( class, obj, [msg] )</tt> Ensures that <tt>obj</tt> is of the <tt>class</tt> type.
+<tt>assert_kind_of( class, obj, [msg] )</tt> Ensures that <tt>obj</tt> is or descends from <tt>class</tt>.
+<tt>assert_respond_to( obj, symbol, [msg] )</tt> Ensures that <tt>obj</tt> has a method called <tt>symbol</tt>.
+<tt>assert_operator( obj1, operator, obj2, [msg] )</tt> Ensures that <tt>obj1.operator(obj2)</tt> is true.
+<tt>assert_send( array, [msg] )</tt> Ensures that executing the method listed in <tt>array[1]</tt> on the object in <tt>array[0]</tt> with the parameters of <tt>array[2 and up]</tt> is true. This one is weird eh?
+<tt>flunk( [msg] )</tt> Ensures failure. This is useful to explicitly mark a test that isn't finished yet.</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt>get<span style="color: #990000">(:</span>show<span style="color: #990000">,</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'id'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">"12"</span><span style="color: #FF0000">}</span><span style="color: #990000">,</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'user_id'</span> <span style="color: #990000">=&gt;</span> <span style="color: #993399">5</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
-</tt></pre></div></div>
-<div class="para"><p>Another example: Calling the <tt>:view</tt> action, passing an <tt>id</tt> of 12 as the <tt>params</tt>, this time with no session, but with a flash message.</p></div>
+<div class="content">
+<pre><tt>
+Because of the modular nature of the testing framework, it is possible to create your own assertions. In fact, that's exactly what Rails does. It includes some specialized assertions to make your life easier.
+
+NOTE: Creating your own assertions is an advanced topic that we won't cover in this tutorial.
+
+=== Rails Specific Assertions ===
+
+Rails adds some custom assertions of its own to the +test/unit+ framework:
+
+[grid="all"]
+`----------------------------------------------------------------------------------`-------------------------------------------------------
+Assertion Purpose</tt></pre>
+</div></div>
+<div class="para"><p><tt>assert_valid(record)</tt> Ensures that the passed record is valid by Active Record standards and returns any error messages if it is not.
+<tt>assert_difference(expressions, difference = 1, message = nil) {|| &#8230;}</tt> Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.
+<tt>assert_no_difference(expressions, message = nil, &amp;block)</tt> Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.
+<tt>assert_recognizes(expected_options, path, extras={}, message=nil)</tt> Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.
+<tt>assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)</tt> Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.
+<tt>assert_response(type, message = nil)</tt> Asserts that the response comes with a specific status code. You can specify <tt>:success</tt> to indicate 200, <tt>:redirect</tt> to indicate 300-399, <tt>:missing</tt> to indicate 404, or <tt>:error</tt> to match the 500-599 range
+<tt>assert_redirected_to(options = {}, message=nil)</tt> Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that <tt>assert_redirected_to(:controller &#8658; "weblog")</tt> will also match the redirection of <tt>redirect_to(:controller &#8658; "weblog", :action &#8658; "show")</tt> and so on.
+<tt>assert_template(expected = nil, message=nil)</tt> Asserts that the request was rendered with the appropriate template file.</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt>get<span style="color: #990000">(:</span>view<span style="color: #990000">,</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'id'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'12'</span><span style="color: #FF0000">}</span><span style="color: #990000">,</span> <span style="font-weight: bold"><span style="color: #0000FF">nil</span></span><span style="color: #990000">,</span> <span style="color: #FF0000">{</span><span style="color: #FF0000">'message'</span> <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'booya!'</span><span style="color: #FF0000">}</span><span style="color: #990000">)</span>
-</tt></pre></div></div>
-<div class="admonitionblock">
-<table><tr>
-<td class="icon">
-<img src="./images/icons/note.png" alt="Note" />
-</td>
-<td class="content">If you try running <tt>test_should_create_post</tt> test from <tt>posts_controller_test.rb</tt> it will fail on account of the newly added model level validation and rightly so.</td>
-</tr></table>
-</div>
-<div class="para"><p>Let us modify <tt>test_should_create_post</tt> test in <tt>posts_controller_test.rb</tt> so that all our test pass:</p></div>
+<div class="content">
+<pre><tt>
+You'll see the usage of some of these assertions in the next chapter.
+
+== Functional Tests for Your Controllers ==
+
+In Rails, testing the various actions of a single controller is called writing functional tests for that controller. Controllers handle the incoming web requests to your application and eventually respond with a rendered view.
+
+=== What to include in your Functional Tests ===
+
+You should test for things such as:
+
+ * was the web request successful?
+ * was the user redirected to the right page?
+ * was the user successfully authenticated?
+ * was the correct object stored in the response template?
+ * was the appropriate message displayed to the user in the view
+
+Now that we have used Rails scaffold generator for our +Post+ resource, it has already created the controller code and functional tests. You can take look at the file +posts_controller_test.rb+ in the +test/functional+ directory.
+
+Let me take you through one such test, +test_should_get_index+ from the file +posts_controller_test.rb+.
+
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>def test_should_get_index
+ get :index
+ assert_response :success
+ assert_not_nil assigns(:posts)
+end</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_create_post
- assert_difference<span style="color: #990000">(</span><span style="color: #FF0000">'Post.count'</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
- post <span style="color: #990000">:</span>create<span style="color: #990000">,</span> <span style="color: #990000">:</span>post <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>title <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'Some title'</span><span style="color: #FF0000">}</span>
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+<div class="content">
+<pre><tt>
+In the +test_should_get_index+ test, Rails simulates a request on the action called index, making sure the request was successful and also ensuring that it assigns a valid +posts+ instance variable.
- assert_redirected_to post_path<span style="color: #990000">(</span>assigns<span style="color: #990000">(:</span>post<span style="color: #990000">))</span>
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-</tt></pre></div></div>
-<div class="para"><p>Now you can try running all the tests and they should pass.</p></div>
-<h3 id="_available_request_types_for_functional_tests">4.2. Available Request Types for Functional Tests</h3>
-<div class="para"><p>If you're familiar with the HTTP protocol, you'll know that <tt>get</tt> is a type of request. There are 5 request types supported in Rails functional tests:</p></div>
-<div class="ilist"><ul>
-<li>
-<p>
-<tt>get</tt>
-</p>
-</li>
-<li>
-<p>
-<tt>post</tt>
-</p>
-</li>
-<li>
-<p>
-<tt>put</tt>
-</p>
-</li>
-<li>
-<p>
-<tt>head</tt>
-</p>
-</li>
-<li>
-<p>
-<tt>delete</tt>
-</p>
-</li>
-</ul></div>
-<div class="para"><p>All of request types are methods that you can use, however, you'll probably end up using the first two more often than the others.</p></div>
-<h3 id="_the_4_hashes_of_the_apocalypse">4.3. The 4 Hashes of the Apocalypse</h3>
-<div class="para"><p>After a request has been made by using one of the 5 methods (<tt>get</tt>, <tt>post</tt>, etc.) and processed, you will have 4 Hash objects ready for use:</p></div>
-<div class="ilist"><ul>
-<li>
-<p>
-<tt>assigns</tt> - Any objects that are stored as instance variables in actions for use in views.
-</p>
-</li>
-<li>
-<p>
-<tt>cookies</tt> - Any cookies that are set.
-</p>
-</li>
-<li>
-<p>
-<tt>flash</tt> - Any objects living in the flash.
-</p>
-</li>
-<li>
-<p>
-<tt>session</tt> - Any object living in session variables.
-</p>
-</li>
-</ul></div>
-<div class="para"><p>As is the case with normal Hash objects, you can access the values by referencing the keys by string. You can also reference them by symbol name, except for <tt>assigns</tt>. For example:</p></div>
+The +get+ method kicks off the web request and populates the results into the response. It accepts 4 arguments:
+
+* The action of the controller you are requesting. This can be in the form of a string or a symbol.
+* An optional hash of request parameters to pass into the action (eg. query string parameters or post variables).
+* An optional hash of session variables to pass along with the request.
+* An optional hash of flash values.
+
+Example: Calling the +:show+ action, passing an +id+ of 12 as the +params+ and setting a +user_id+ of 5 in the session:
+
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>get(:show, {<em>id</em> &#8658; "12"}, {<em>user_id</em> &#8658; 5})</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt> flash<span style="color: #990000">[</span><span style="color: #FF0000">"gordon"</span><span style="color: #990000">]</span> flash<span style="color: #990000">[:</span>gordon<span style="color: #990000">]</span>
- session<span style="color: #990000">[</span><span style="color: #FF0000">"shmession"</span><span style="color: #990000">]</span> session<span style="color: #990000">[:</span>shmession<span style="color: #990000">]</span>
- cookies<span style="color: #990000">[</span><span style="color: #FF0000">"are_good_for_u"</span><span style="color: #990000">]</span> cookies<span style="color: #990000">[:</span>are_good_for_u<span style="color: #990000">]</span>
+<div class="content">
+<pre><tt>
+Another example: Calling the +:view+ action, passing an +id+ of 12 as the +params+, this time with no session, but with a flash message.
-<span style="font-style: italic"><span style="color: #9A1900"># Because you can't use assigns[:something] for historical reasons:</span></span>
- assigns<span style="color: #990000">[</span><span style="color: #FF0000">"something"</span><span style="color: #990000">]</span> assigns<span style="color: #990000">(:</span>something<span style="color: #990000">)</span>
-</tt></pre></div></div>
-<h3 id="_instance_variables_available">4.4. Instance Variables Available</h3>
-<div class="para"><p>You also have access to three instance variables in your functional tests:</p></div>
-<div class="ilist"><ul>
-<li>
-<p>
-<tt>@controller</tt> - The controller processing the request
-</p>
-</li>
-<li>
-<p>
-<tt>@request</tt> - The request
-</p>
-</li>
-<li>
-<p>
-<tt>@response</tt> - The response
-</p>
-</li>
-</ul></div>
-<h3 id="_a_fuller_functional_test_example">4.5. A Fuller Functional Test Example</h3>
-<div class="para"><p>Here's another example that uses <tt>flash</tt>, <tt>assert_redirected_to</tt>, and <tt>assert_difference</tt>:</p></div>
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>get(:view, {<em>id</em> &#8658; <em>12</em>}, nil, {<em>message</em> &#8658; <em>booya!</em>})</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_should_create_post
- assert_difference<span style="color: #990000">(</span><span style="color: #FF0000">'Post.count'</span><span style="color: #990000">)</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
- post <span style="color: #990000">:</span>create<span style="color: #990000">,</span> <span style="color: #990000">:</span>post <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">{</span> <span style="color: #990000">:</span>title <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'Hi'</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>body <span style="color: #990000">=&gt;</span> <span style="color: #FF0000">'This is my first post.'</span><span style="color: #FF0000">}</span>
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
- assert_redirected_to post_path<span style="color: #990000">(</span>assigns<span style="color: #990000">(:</span>post<span style="color: #990000">))</span>
- assert_equal <span style="color: #FF0000">'Post was successfully created.'</span><span style="color: #990000">,</span> flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span>
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-</tt></pre></div></div>
-<h3 id="_testing_views">4.6. Testing Views</h3>
-<div class="para"><p>Testing the response to your request by asserting the presence of key HTML elements and their content is a useful way to test the views of your application. The <tt>assert_select</tt> assertion allows you to do this by using a simple yet powerful syntax.</p></div>
-<div class="admonitionblock">
-<table><tr>
-<td class="icon">
-<img src="./images/icons/note.png" alt="Note" />
-</td>
-<td class="content">You may find references to <tt>assert_tag</tt> in other documentation, but this is now deprecated in favor of <tt>assert_select</tt>.</td>
-</tr></table>
-</div>
-<div class="para"><p>There are two forms of <tt>assert_select</tt>:</p></div>
-<div class="para"><p><tt>assert_select(selector, [equality], [message])`</tt> ensures that the equality condition is met on the selected elements through the selector. The selector may be a CSS selector expression (String), an expression with substitution values, or an <tt>HTML::Selector</tt> object.</p></div>
-<div class="para"><p><tt>assert_select(element, selector, [equality], [message])</tt> ensures that the equality condition is met on all the selected elements through the selector starting from the <em>element</em> (instance of <tt>HTML::Node</tt>) and its descendants.</p></div>
-<div class="para"><p>For example, you could verify the contents on the title element in your response with:</p></div>
+<div class="content">
+<pre><tt>
+NOTE: If you try running +test_should_create_post+ test from +posts_controller_test.rb+ it will fail on account of the newly added model level validation and rightly so.
+
+Let us modify +test_should_create_post+ test in +posts_controller_test.rb+ so that all our test pass:
+
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>def test_should_create_post
+ assert_difference(<em>Post.count</em>) do
+ post :create, :post &#8658; { :title &#8658; <em>Some title</em>}
+ end</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt> assert_redirected_to post_path(assigns(:post))
+end</tt></pre>
+</div></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt>assert_select <span style="color: #FF0000">'title'</span><span style="color: #990000">,</span> <span style="color: #FF0000">"Welcome to Rails Testing Guide"</span>
-</tt></pre></div></div>
-<div class="para"><p>You can also use nested <tt>assert_select</tt> blocks. In this case the inner <tt>assert_select</tt> will run the assertion on each element selected by the outer <tt>assert_select</tt> block:</p></div>
+<div class="content">
+<pre><tt>
+Now you can try running all the tests and they should pass.
+
+=== Available Request Types for Functional Tests ===
+
+If you're familiar with the HTTP protocol, you'll know that +get+ is a type of request. There are 5 request types supported in Rails functional tests:
+
+* +get+
+* +post+
+* +put+
+* +head+
+* +delete+
+
+All of request types are methods that you can use, however, you'll probably end up using the first two more often than the others.
+
+=== The 4 Hashes of the Apocalypse ===
+
+After a request has been made by using one of the 5 methods (+get+, +post+, etc.) and processed, you will have 4 Hash objects ready for use:
+
+* +assigns+ - Any objects that are stored as instance variables in actions for use in views.
+* +cookies+ - Any cookies that are set.
+* +flash+ - Any objects living in the flash.
+* +session+ - Any object living in session variables.
+
+As is the case with normal Hash objects, you can access the values by referencing the keys by string. You can also reference them by symbol name, except for +assigns+. For example:
+
+[source,ruby]</tt></pre>
+</div></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>flash["gordon"] flash[:gordon]
+session["shmession"] session[:shmession]
+cookies["are_good_for_u"] cookies[:are_good_for_u]</tt></pre>
+</div></div>
+<div class="para"><p># Because you can't use assigns[:something] for historical reasons:
+ assigns["something"] assigns(:something)</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt>assert_select <span style="color: #FF0000">'ul.navigation'</span> <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
- assert_select <span style="color: #FF0000">'li.menu_item'</span>
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-</tt></pre></div></div>
-<div class="para"><p>The <tt>assert_select</tt> assertion is quite powerful. For more advanced usage, refer to its <a href="http://api.rubyonrails.com/classes/ActionController/Assertions/SelectorAssertions.html#M000749">documentation</a>.</p></div>
-<h4 id="_additional_view_based_assertions">4.6.1. Additional View-based Assertions</h4>
-<div class="para"><p>There are more assertions that are primarily used in testing views:</p></div>
-<div class="tableblock">
-<table rules="all"
-frame="hsides"
-cellspacing="0" cellpadding="4">
-<col width="948" />
-<col width="640" />
-<thead>
- <tr>
- <th align="left">
- Assertion
- </th>
- <th align="left">
- Purpose
- </th>
- </tr>
-</thead>
-<tbody valign="top">
- <tr>
- <td align="left">
- <tt>assert_select_email</tt>
- </td>
- <td align="left">
- Allows you to make assertions on the body of an e-mail.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_select_rjs</tt>
- </td>
- <td align="left">
- Allows you to make assertions on RJS response. <tt>assert_select_rjs</tt> has variants which allow you to narrow down on the updated element or even a particular operation on an element.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>assert_select_encoded</tt>
- </td>
- <td align="left">
- Allows you to make assertions on encoded HTML. It does this by un-encoding the contents of each element and then calling the block with all the un-encoded elements.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>css_select(selector)</tt> or <tt>css_select(element, selector)</tt>
- </td>
- <td align="left">
- Returns an array of all the elements selected by the <em>selector</em>. In the second variant it first matches the base <em>element</em> and tries to match the <em>selector</em> expression on any of its children. If there are no matches both variants return an empty array.
- </td>
- </tr>
-</tbody>
-</table>
-</div>
-<div class="para"><p>Here's an example of using <tt>assert_select_email</tt>:</p></div>
+<div class="content">
+<pre><tt>
+=== Instance Variables Available ===
+
+You also have access to three instance variables in your functional tests:
+
+* +@controller+ - The controller processing the request
+* +@request+ - The request
+* +@response+ - The response
+
+=== A Fuller Functional Test Example
+
+Here's another example that uses +flash+, +assert_redirected_to+, and +assert_difference+:
+
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>def test_should_create_post
+ assert_difference(<em>Post.count</em>) do
+ post :create, :post &#8658; { :title &#8658; <em>Hi</em>, :body &#8658; <em>This is my first post.</em>}
+ end
+ assert_redirected_to post_path(assigns(:post))
+ assert_equal <em>Post was successfully created.</em>, flash[:notice]
+end</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt>assert_select_email <span style="font-weight: bold"><span style="color: #0000FF">do</span></span>
- assert_select <span style="color: #FF0000">'small'</span><span style="color: #990000">,</span> <span style="color: #FF0000">'Please click the "Unsubscribe" link if you want to opt-out.'</span>
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-</tt></pre></div></div>
-</div>
-<h2 id="_integration_testing">5. Integration Testing</h2>
-<div class="sectionbody">
-<div class="para"><p>Integration tests are used to test the interaction among any number of controllers. They are generally used to test important work flows within your application.</p></div>
-<div class="para"><p>Unlike Unit and Functional tests, integration tests have to be explicitly created under the <em>test/integration</em> folder within your application. Rails provides a generator to create an integration test skeleton for you.</p></div>
+<div class="content">
+<pre><tt>
+=== Testing Views ===
+
+Testing the response to your request by asserting the presence of key HTML elements and their content is a useful way to test the views of your application. The +assert_select+ assertion allows you to do this by using a simple yet powerful syntax.
+
+NOTE: You may find references to +assert_tag+ in other documentation, but this is now deprecated in favor of +assert_select+.
+
+There are two forms of +assert_select+:
+
++assert_select(selector, [equality], [message])`+ ensures that the equality condition is met on the selected elements through the selector. The selector may be a CSS selector expression (String), an expression with substitution values, or an +HTML::Selector+ object.
+
++assert_select(element, selector, [equality], [message])+ ensures that the equality condition is met on all the selected elements through the selector starting from the _element_ (instance of +HTML::Node+) and its descendants.
+
+For example, you could verify the contents on the title element in your response with:
+
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>assert_select <em>title</em>, "Welcome to Rails Testing Guide"</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt>$ script/generate integration_test user_flows
- exists test/integration<span style="color: #990000">/</span>
- create test/integration/user_flows_test<span style="color: #990000">.</span>rb
-</tt></pre></div></div>
-<div class="para"><p>Here's what a freshly-generated integration test looks like:</p></div>
+<div class="content">
+<pre><tt>
+You can also use nested +assert_select+ blocks. In this case the inner +assert_select+ will run the assertion on each element selected by the outer `assert_select` block:
+
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>assert_select <em>ul.navigation</em> do
+ assert_select <em>li.menu_item</em>
+end</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+<div class="content">
+<pre><tt>
+The +assert_select+ assertion is quite powerful. For more advanced usage, refer to its link:http://api.rubyonrails.com/classes/ActionController/Assertions/SelectorAssertions.html#M000749[documentation].
-<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserFlowsTest <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>IntegrationTest
- <span style="font-style: italic"><span style="color: #9A1900"># fixtures :your, :models</span></span>
+==== Additional View-based Assertions ====
- <span style="font-style: italic"><span style="color: #9A1900"># Replace this with your real tests.</span></span>
- <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_truth
- assert <span style="font-weight: bold"><span style="color: #0000FF">true</span></span>
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-</tt></pre></div></div>
-<div class="para"><p>Integration tests inherit from <tt>ActionController::IntegrationTest</tt>. This makes available some additional helpers to use in your integration tests. Also you need to explicitly include the fixtures to be made available to the test.</p></div>
-<h3 id="_helpers_available_for_integration_tests">5.1. Helpers Available for Integration tests</h3>
-<div class="para"><p>In addition to the standard testing helpers, there are some additional helpers available to integration tests:</p></div>
-<div class="tableblock">
-<table rules="all"
-frame="hsides"
-cellspacing="0" cellpadding="4">
-<col width="948" />
-<col width="640" />
-<thead>
- <tr>
- <th align="left">
- Helper
- </th>
- <th align="left">
- Purpose
- </th>
- </tr>
-</thead>
-<tbody valign="top">
- <tr>
- <td align="left">
- <tt>https?</tt>
- </td>
- <td align="left">
- Returns <tt>true</tt> if the session is mimicking a secure HTTPS request.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>https!</tt>
- </td>
- <td align="left">
- Allows you to mimic a secure HTTPS request.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>host!</tt>
- </td>
- <td align="left">
- Allows you to set the host name to use in the next request.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>redirect?</tt>
- </td>
- <td align="left">
- Returns <tt>true</tt> if the last request was a redirect.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>follow_redirect!</tt>
- </td>
- <td align="left">
- Follows a single redirect response.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>request_via_redirect(http_method, path, [parameters], [headers])</tt>
- </td>
- <td align="left">
- Allows you to make an HTTP request and follow any subsequent redirects.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>post_via_redirect(path, [parameters], [headers])</tt>
- </td>
- <td align="left">
- Allows you to make an HTTP POST request and follow any subsequent redirects.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>get_via_redirect(path, [parameters], [headers])</tt>
- </td>
- <td align="left">
- Allows you to make an HTTP GET request and follow any subsequent redirects.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>put_via_redirect(path, [parameters], [headers])</tt>
- </td>
- <td align="left">
- Allows you to make an HTTP PUT request and follow any subsequent redirects.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>delete_via_redirect(path, [parameters], [headers])</tt>
- </td>
- <td align="left">
- Allows you to make an HTTP DELETE request and follow any subsequent redirects.
- </td>
- </tr>
- <tr>
- <td align="left">
- <tt>open_session</tt>
- </td>
- <td align="left">
- Opens a new session instance.
- </td>
- </tr>
-</tbody>
-</table>
-</div>
-<h3 id="_integration_testing_examples">5.2. Integration Testing Examples</h3>
-<div class="para"><p>A simple integration test that exercises multiple controllers:</p></div>
+There are more assertions that are primarily used in testing views:
+
+[grid="all"]
+`----------------------------------------------------------------------------------`-------------------------------------------------------
+Assertion Purpose</tt></pre>
+</div></div>
+<div class="para"><p><tt>assert_select_email</tt> Allows you to make assertions on the body of an e-mail.
+<tt>assert_select_rjs</tt> Allows you to make assertions on RJS response. <tt>assert_select_rjs</tt> has variants which allow you to narrow down on the updated element or even a particular operation on an element.
+<tt>assert_select_encoded</tt> Allows you to make assertions on encoded HTML. It does this by un-encoding the contents of each element and then calling the block with all the un-encoded elements.
+<tt>css_select(selector)</tt> or <tt>css_select(element, selector)</tt> Returns an array of all the elements selected by the <em>selector</em>. In the second variant it first matches the base <em>element</em> and tries to match the <em>selector</em> expression on any of its children. If there are no matches both variants return an empty array.</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+<div class="content">
+<pre><tt>
+Here's an example of using +assert_select_email+:
-<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserFlowsTest <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>IntegrationTest
- fixtures <span style="color: #990000">:</span>users
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>assert_select_email do
+ assert_select <em>small</em>, <em>Please click the "Unsubscribe" link if you want to opt-out.</em>
+end</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>
+== Integration Testing ==
- <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_login_and_browse_site
- <span style="font-style: italic"><span style="color: #9A1900"># login via https</span></span>
- https!
- get <span style="color: #FF0000">"/login"</span>
- assert_response <span style="color: #990000">:</span>success
+Integration tests are used to test the interaction among any number of controllers. They are generally used to test important work flows within your application.
- post_via_redirect <span style="color: #FF0000">"/login"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>username <span style="color: #990000">=&gt;</span> users<span style="color: #990000">(:</span>avs<span style="color: #990000">).</span>username<span style="color: #990000">,</span> <span style="color: #990000">:</span>password <span style="color: #990000">=&gt;</span> users<span style="color: #990000">(:</span>avs<span style="color: #990000">).</span>password
- assert_equal <span style="color: #FF0000">'/welcome'</span><span style="color: #990000">,</span> path
- assert_equal <span style="color: #FF0000">'Welcome avs!'</span><span style="color: #990000">,</span> flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span>
+Unlike Unit and Functional tests, integration tests have to be explicitly created under the 'test/integration' folder within your application. Rails provides a generator to create an integration test skeleton for you.
- https!<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">false</span></span><span style="color: #990000">)</span>
- get <span style="color: #FF0000">"/posts/all"</span>
- assert_response <span style="color: #990000">:</span>success
- assert assigns<span style="color: #990000">(:</span>products<span style="color: #990000">)</span>
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-</tt></pre></div></div>
-<div class="para"><p>As you can see the integration test involves multiple controllers and exercises the entire stack from database to dispatcher. In addition you can have multiple session instances open simultaneously in a test and extend those instances with assertion methods to create a very powerful testing DSL (domain-specific language) just for your application.</p></div>
-<div class="para"><p>Here's an example of multiple sessions and custom DSL in an integration test</p></div>
+[source, shell]</tt></pre>
+</div></div>
+<div class="para"><p>$ script/generate integration_test user_flows
+ exists test/integration/
+ create test/integration/user_flows_test.rb</p></div>
<div class="listingblock">
-<div class="content"><!-- Generator: GNU source-highlight 2.9
-by Lorenzo Bettini
-http://www.lorenzobettini.it
-http://www.gnu.org/software/src-highlite -->
-<pre><tt><span style="font-weight: bold"><span style="color: #000080">require</span></span> <span style="color: #FF0000">'test_helper'</span>
+<div class="content">
+<pre><tt>
+Here's what a freshly-generated integration test looks like:
-<span style="font-weight: bold"><span style="color: #0000FF">class</span></span> UserFlowsTest <span style="color: #990000">&lt;</span> ActionController<span style="color: #990000">::</span>IntegrationTest
- fixtures <span style="color: #990000">:</span>users
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>require <em>test_helper</em></p></div>
+<div class="para"><p>class UserFlowsTest &lt; ActionController::IntegrationTest
+ # fixtures :your, :models</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt> # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end</tt></pre>
+</div></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>
+Integration tests inherit from +ActionController::IntegrationTest+. This makes available some additional helpers to use in your integration tests. Also you need to explicitly include the fixtures to be made available to the test.
- <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> test_login_and_browse_site
+=== Helpers Available for Integration tests ===
- <span style="font-style: italic"><span style="color: #9A1900"># User avs logs in</span></span>
- avs <span style="color: #990000">=</span> login<span style="color: #990000">(:</span>avs<span style="color: #990000">)</span>
- <span style="font-style: italic"><span style="color: #9A1900"># User guest logs in</span></span>
- guest <span style="color: #990000">=</span> login<span style="color: #990000">(:</span>guest<span style="color: #990000">)</span>
+In addition to the standard testing helpers, there are some additional helpers available to integration tests:
- <span style="font-style: italic"><span style="color: #9A1900"># Both are now available in different sessions</span></span>
- assert_equal <span style="color: #FF0000">'Welcome avs!'</span><span style="color: #990000">,</span> avs<span style="color: #990000">.</span>flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span>
- assert_equal <span style="color: #FF0000">'Welcome guest!'</span><span style="color: #990000">,</span> guest<span style="color: #990000">.</span>flash<span style="color: #990000">[:</span>notice<span style="color: #990000">]</span>
+[grid="all"]
+`----------------------------------------------------------------------------------`-------------------------------------------------------
+Helper Purpose</tt></pre>
+</div></div>
+<div class="para"><p><tt>https?</tt> Returns <tt>true</tt> if the session is mimicking a secure HTTPS request.
+<tt>https!</tt> Allows you to mimic a secure HTTPS request.
+<tt>host!</tt> Allows you to set the host name to use in the next request.
+<tt>redirect?</tt> Returns <tt>true</tt> if the last request was a redirect.
+<tt>follow_redirect!</tt> Follows a single redirect response.
+<tt>request_via_redirect(http_method, path, [parameters], [headers])</tt> Allows you to make an HTTP request and follow any subsequent redirects.
+<tt>post_via_redirect(path, [parameters], [headers])</tt> Allows you to make an HTTP POST request and follow any subsequent redirects.
+<tt>get_via_redirect(path, [parameters], [headers])</tt> Allows you to make an HTTP GET request and follow any subsequent redirects.
+<tt>put_via_redirect(path, [parameters], [headers])</tt> Allows you to make an HTTP PUT request and follow any subsequent redirects.
+<tt>delete_via_redirect(path, [parameters], [headers])</tt> Allows you to make an HTTP DELETE request and follow any subsequent redirects.
+<tt>open_session</tt> Opens a new session instance.</p></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>
+=== Integration Testing Examples ===
- <span style="font-style: italic"><span style="color: #9A1900"># User avs can browse site</span></span>
- avs<span style="color: #990000">.</span>browses_site
- <span style="font-style: italic"><span style="color: #9A1900"># User guest can browse site aswell</span></span>
- guest<span style="color: #990000">.</span>browses_site
+A simple integration test that exercises multiple controllers:
- <span style="font-style: italic"><span style="color: #9A1900"># Continue with other assertions</span></span>
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>require <em>test_helper</em></p></div>
+<div class="para"><p>class UserFlowsTest &lt; ActionController::IntegrationTest
+ fixtures :users</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>def test_login_and_browse_site
+ # login via https
+ https!
+ get "/login"
+ assert_response :success</tt></pre>
+</div></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>post_via_redirect "/login", :username =&gt; users(:avs).username, :password =&gt; users(:avs).password
+assert_equal '/welcome', path
+assert_equal 'Welcome avs!', flash[:notice]</tt></pre>
+</div></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt> https!(false)
+ get "/posts/all"
+ assert_response :success
+ assert assigns(:products)
+ end
+end</tt></pre>
+</div></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>
+As you can see the integration test involves multiple controllers and exercises the entire stack from database to dispatcher. In addition you can have multiple session instances open simultaneously in a test and extend those instances with assertion methods to create a very powerful testing DSL (domain-specific language) just for your application.
- private
+Here's an example of multiple sessions and custom DSL in an integration test
- <span style="font-weight: bold"><span style="color: #0000FF">module</span></span> CustomDsl
- <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> browses_site
- get <span style="color: #FF0000">"/products/all"</span>
- assert_response <span style="color: #990000">:</span>success
- assert assigns<span style="color: #990000">(:</span>products<span style="color: #990000">)</span>
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
+[source,ruby]</tt></pre>
+</div></div>
+<div class="para"><p>require <em>test_helper</em></p></div>
+<div class="para"><p>class UserFlowsTest &lt; ActionController::IntegrationTest
+ fixtures :users</p></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>def test_login_and_browse_site</tt></pre>
+</div></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt># User avs logs in
+avs = login(:avs)
+# User guest logs in
+guest = login(:guest)</tt></pre>
+</div></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt># Both are now available in different sessions
+assert_equal 'Welcome avs!', avs.flash[:notice]
+assert_equal 'Welcome guest!', guest.flash[:notice]</tt></pre>
+</div></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt># User avs can browse site
+avs.browses_site
+# User guest can browse site aswell
+guest.browses_site</tt></pre>
+</div></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt> # Continue with other assertions
+end</tt></pre>
+</div></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>private</tt></pre>
+</div></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt>module CustomDsl
+ def browses_site
+ get "/products/all"
+ assert_response :success
+ assert assigns(:products)
+ end
+end</tt></pre>
+</div></div>
+<div class="literalblock">
+<div class="content">
+<pre><tt> def login(user)
+ open_session do |sess|
+ sess.extend(CustomDsl)
+ u = users(user)
+ sess.https!
+ sess.post "/login", :username =&gt; u.username, :password =&gt; u.password
+ assert_equal '/welcome', path
+ sess.https!(false)
+ end
+ end
+end</tt></pre>
+</div></div>
+<div class="listingblock">
+<div class="content">
+<pre><tt>
+== Rake Tasks for Running your Tests ==
- <span style="font-weight: bold"><span style="color: #0000FF">def</span></span> login<span style="color: #990000">(</span>user<span style="color: #990000">)</span>
- open_session <span style="font-weight: bold"><span style="color: #0000FF">do</span></span> <span style="color: #990000">|</span>sess<span style="color: #990000">|</span>
- sess<span style="color: #990000">.</span>extend<span style="color: #990000">(</span>CustomDsl<span style="color: #990000">)</span>
- u <span style="color: #990000">=</span> users<span style="color: #990000">(</span>user<span style="color: #990000">)</span>
- sess<span style="color: #990000">.</span>https!
- sess<span style="color: #990000">.</span>post <span style="color: #FF0000">"/login"</span><span style="color: #990000">,</span> <span style="color: #990000">:</span>username <span style="color: #990000">=&gt;</span> u<span style="color: #990000">.</span>username<span style="color: #990000">,</span> <span style="color: #990000">:</span>password <span style="color: #990000">=&gt;</span> u<span style="color: #990000">.</span>password
- assert_equal <span style="color: #FF0000">'/welcome'</span><span style="color: #990000">,</span> path
- sess<span style="color: #990000">.</span>https!<span style="color: #990000">(</span><span style="font-weight: bold"><span style="color: #0000FF">false</span></span><span style="color: #990000">)</span>
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
- <span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
-</tt></pre></div></div>
-</div>
-<h2 id="_rake_tasks_for_running_your_tests">6. Rake Tasks for Running your Tests</h2>
-<div class="sectionbody">
-<div class="para"><p>You don't need to set up and run your tests by hand on a test-by-test basis. Rails comes with a number of rake tasks to help in testing. The table below lists all rake tasks that come along in the default Rakefile when you initiate a Rail project.</p></div>
-<div class="para"><p>--------------------------------`----------------------------------------------------
-Tasks Description</p></div>
+You don't need to set up and run your tests by hand on a test-by-test basis. Rails comes with a number of rake tasks to help in testing. The table below lists all rake tasks that come along in the default Rakefile when you initiate a Rail project.
+
+[grid="all"]</tt></pre>
+</div></div>
+<div class="para"><p>Tasks Description</p></div>
<div class="listingblock">
<div class="content">
<pre><tt>+rake test+ Runs all unit, functional and integration tests. You can also simply run +rake+ as the _test_ target is the default.
@@ -1584,7 +1146,7 @@ Tasks Description</p></div>
+rake test:plugins+ Run all the plugin tests from +vendor/plugins/*/**/test+ (or specify with +PLUGIN=_name_+)</tt></pre>
</div></div>
</div>
-<h2 id="_brief_note_about_test_unit">7. Brief Note About Test::Unit</h2>
+<h2 id="_brief_note_about_test_unit">4. Brief Note About Test::Unit</h2>
<div class="sectionbody">
<div class="para"><p>Ruby ships with a boat load of libraries. One little gem of a library is <tt>Test::Unit</tt>, a framework for unit testing in Ruby. All the basic assertions discussed above are actually defined in <tt>Test::Unit::Assertions</tt>. The class <tt>ActiveSupport::TestCase</tt> which we have been using in our unit and functional tests extends <tt>Test::Unit::TestCase</tt> that it is how we can use all the basic assertions in our tests.</p></div>
<div class="admonitionblock">
@@ -1596,7 +1158,7 @@ Tasks Description</p></div>
</tr></table>
</div>
</div>
-<h2 id="_setup_and_teardown">8. Setup and Teardown</h2>
+<h2 id="_setup_and_teardown">5. Setup and Teardown</h2>
<div class="sectionbody">
<div class="para"><p>If you would like to run a block of code before the start of each test and another block of code after the end of each test you have two special callbacks for your rescue. Let's take note of this by looking at an example for our functional test in <tt>Posts</tt> controller:</p></div>
<div class="listingblock">
@@ -1704,7 +1266,7 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
</div>
-<h2 id="_testing_routes">9. Testing Routes</h2>
+<h2 id="_testing_routes">6. Testing Routes</h2>
<div class="sectionbody">
<div class="para"><p>Like everything else in you Rails application, it's recommended to test you routes. An example test for a route in the default <tt>show</tt> action of <tt>Posts</tt> controller above should look like:</p></div>
<div class="listingblock">
@@ -1717,10 +1279,10 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
</div>
-<h2 id="_testing_your_mailers">10. Testing Your Mailers</h2>
+<h2 id="_testing_your_mailers">7. Testing Your Mailers</h2>
<div class="sectionbody">
<div class="para"><p>Testing mailer classes requires some specific tools to do a thorough job.</p></div>
-<h3 id="_keeping_the_postman_in_check">10.1. Keeping the Postman in Check</h3>
+<h3 id="_keeping_the_postman_in_check">7.1. Keeping the Postman in Check</h3>
<div class="para"><p>Your <tt>ActionMailer</tt> classes &#8212; like every other part of your Rails application &#8212; should be tested to ensure that it is working as expected.</p></div>
<div class="para"><p>The goals of testing your <tt>ActionMailer</tt> classes are to ensure that:</p></div>
<div class="ilist"><ul>
@@ -1740,14 +1302,14 @@ the right emails are being sent at the right times
</p>
</li>
</ul></div>
-<h4 id="_from_all_sides">10.1.1. From All Sides</h4>
+<h4 id="_from_all_sides">7.1.1. From All Sides</h4>
<div class="para"><p>There are two aspects of testing your mailer, the unit tests and the functional tests. In the unit tests, you run the mailer in isolation with tightly controlled inputs and compare the output to a knownvalue (a fixture &#8212; yay! more fixtures!). In the functional tests you don't so much test the minute details produced by the mailer Instead we test that our controllers and models are using the mailer in the right way. You test to prove that the right email was sent at the right time.</p></div>
-<h3 id="_unit_testing">10.2. Unit Testing</h3>
+<h3 id="_unit_testing">7.2. Unit Testing</h3>
<div class="para"><p>In order to test that your mailer is working as expected, you can use unit tests to compare the actual results of the mailer with pre-written examples of what should be produced.</p></div>
-<h4 id="_revenge_of_the_fixtures">10.2.1. Revenge of the Fixtures</h4>
+<h4 id="_revenge_of_the_fixtures">7.2.1. Revenge of the Fixtures</h4>
<div class="para"><p>For the purposes of unit testing a mailer, fixtures are used to provide an example of how the output <em>should</em> look. Because these are example emails, and not Active Record data like the other fixtures, they are kept in their own subdirectory apart from the other fixtures. The name of the directory within <tt>test/fixtures</tt> directly corresponds to the name of the mailer. So, for a mailer named <tt>UserMailer</tt>, the fixtures should reside in <tt>test/fixtures/user_mailer</tt> directory.</p></div>
<div class="para"><p>When you generated your mailer, the generator creates stub fixtures for each of the mailers actions. If you didn't use the generator you'll have to make those files yourself.</p></div>
-<h4 id="_the_basic_test_case">10.2.2. The Basic Test case</h4>
+<h4 id="_the_basic_test_case">7.2.2. The Basic Test case</h4>
<div class="para"><p>Here's a unit test to test a mailer named <tt>UserMailer</tt> whose action <tt>invite</tt> is used to send an invitation to a friend. It is an adapted version of the base test created by the generator for an <tt>invite</tt> action.</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
@@ -1782,7 +1344,7 @@ Cheers!</tt></pre>
</div></div>
<div class="para"><p>This is the right time to understand a little more about writing tests for your mailers. The line <tt>ActionMailer::Base.delivery_method = :test</tt> in <tt>config/environments/test.rb</tt> sets the delivery method to test mode so that email will not actually be delivered (useful to avoid spamming your users while testing) but instead it will be appended to an array (<tt>ActionMailer::Base.deliveries</tt>).</p></div>
<div class="para"><p>However often in unit tests, mails will not actually be sent, simply constructed, as in the example above, where the precise content of the email is checked against what it should be.</p></div>
-<h3 id="_functional_testing">10.3. Functional Testing</h3>
+<h3 id="_functional_testing">7.3. Functional Testing</h3>
<div class="para"><p>Functional testing for mailers involves more than just checking that the email body, recipients and so forth are correct. In functional mail tests you call the mail deliver methods and check that the appropriate emails have been appended to the delivery list. It is fairly safe to assume that the deliver methods themselves do their job You are probably more interested in is whether your own business logic is sending emails when you expect them to got out. For example, you can check that the invite friend operation is sending an email appropriately:</p></div>
<div class="listingblock">
<div class="content"><!-- Generator: GNU source-highlight 2.9
@@ -1805,7 +1367,7 @@ http://www.gnu.org/software/src-highlite -->
<span style="font-weight: bold"><span style="color: #0000FF">end</span></span>
</tt></pre></div></div>
</div>
-<h2 id="_other_testing_approaches">11. Other Testing Approaches</h2>
+<h2 id="_other_testing_approaches">8. Other Testing Approaches</h2>
<div class="sectionbody">
<div class="para"><p>The built-in <tt>test/unit</tt> based testing is not the only way to test Rails applications. Rails developers have come up with a wide variety of other approaches and aids for testing, including:</p></div>
<div class="ilist"><ul>
@@ -1831,7 +1393,7 @@ link: <a href="http://rspec.info/">RSpec</a>, a behavior-driven development fram
</li>
</ul></div>
</div>
-<h2 id="_changelog">12. Changelog</h2>
+<h2 id="_changelog">9. Changelog</h2>
<div class="sectionbody">
<div class="para"><p><a href="http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/8">Lighthouse ticket</a></p></div>
<div class="ilist"><ul>
diff --git a/railties/doc/guides/source/activerecord_validations_callbacks.txt b/railties/doc/guides/source/activerecord_validations_callbacks.txt
index 0c82f24e66..e0bb534d0b 100644
--- a/railties/doc/guides/source/activerecord_validations_callbacks.txt
+++ b/railties/doc/guides/source/activerecord_validations_callbacks.txt
@@ -47,7 +47,7 @@ We can see how it works by looking at the following script/console output:
=> false
------------------------------------------------------------------
-Saving new records means sending an SQL insert operation to the database, while saving existing records (by calling either +save+ or +update_attributes+) will result in a SQL update operation. Active Record will use this facts to perform validations upon your objects, avoiding then to be recorded to the database if their inner state is invalid in some way. You can specify validations that will be beformed every time a object is saved, just when you're creating a new record or when you're updating an existing one.
+Saving new records means sending an SQL insert operation to the database, while saving existing records (by calling either +save+ or +update_attributes+) will result in a SQL update operation. Active Record will use these facts to perform validations upon your objects, avoiding then to be recorded to the database if their inner state is invalid in some way. You can specify validations that will be beformed every time a object is saved, just when you're creating a new record or when you're updating an existing one.
CAUTION: There are four methods that when called will trigger validation: +save+, +save!+, +update_attributes+ and +update_attributes!+. There is one method left, which is +update_attribute+. This method will update the value of an attribute without triggering any validation, so be careful when using +update_attribute+, since it can let you save your objects in an invalid state.
@@ -155,7 +155,8 @@ This helper validates that the attributes' values are not included in a given se
[source, ruby]
------------------------------------------------------------------
class MovieFile < ActiveRecord::Base
- validates_exclusion_of :format, :in => %w(mov avi), :message => "Extension %s is not allowed"
+ validates_exclusion_of :format, :in => %w(mov avi),
+ :message => "Extension %s is not allowed"
end
------------------------------------------------------------------
@@ -170,7 +171,8 @@ This helper validates the attributes's values by testing if they match a given p
[source, ruby]
------------------------------------------------------------------
class Product < ActiveRecord::Base
- validates_format_of :description, :with => /^[a-zA-Z]+$/, :message => "Only letters allowed"
+ validates_format_of :description, :with => /^[a-zA-Z]+$/,
+ :message => "Only letters allowed"
end
------------------------------------------------------------------
@@ -183,7 +185,8 @@ This helper validates that the attributes' values are included in a given set. I
[source, ruby]
------------------------------------------------------------------
class Coffee < ActiveRecord::Base
- validates_inclusion_of :size, :in => %w(small medium large), :message => "%s is not a valid size"
+ validates_inclusion_of :size, :in => %w(small medium large),
+ :message => "%s is not a valid size"
end
------------------------------------------------------------------
@@ -223,7 +226,7 @@ end
This helper has an alias called +validates_size_of+, it's the same helper with a different name. You can use it if you'd like to.
-=== The +validates_numericallity_of+ helper
+=== The +validates_numericality_of+ helper
This helper validates that your attributes have only numeric values. By default, it will match an optional sign followed by a integral or floating point number. Using the +:integer_only+ option set to true, you can specify that only integral numbers are allowed.
@@ -232,12 +235,12 @@ If you use +:integer_only+ set to +true+, then it will use the +$$/\A[+\-]?\d+\Z
[source, ruby]
------------------------------------------------------------------
class Player < ActiveRecord::Base
- validates_numericallity_of :points
- validates_numericallity_of :games_played, :integer_only => true
+ validates_numericality_of :points
+ validates_numericality_of :games_played, :integer_only => true
end
------------------------------------------------------------------
-The default error message for +validates_numericallity_of+ is "_is not a number_".
+The default error message for +validates_numericality_of+ is "_is not a number_".
=== The +validates_presence_of+ helper
@@ -282,7 +285,8 @@ There is a +:scope+ option that you can use to specify other attributes that mus
[source, ruby]
------------------------------------------------------------------
class Holiday < ActiveRecord::Base
- validates_uniqueness_of :name, :scope => :year, :message => "Should happen once per year"
+ validates_uniqueness_of :name, :scope => :year,
+ :message => "Should happen once per year"
end
------------------------------------------------------------------
@@ -324,9 +328,14 @@ As stated before, the +:on+ option lets you specify when the validation should h
[source, ruby]
------------------------------------------------------------------
class Person < ActiveRecord::Base
- validates_uniqueness_of :email, :on => :create # => it will be possible to update email with a duplicated value
- validates_numericallity_of :age, :on => :update # => it will be possible to create the record with a 'non-numerical age'
- validates_presence_of :name, :on => :save # => that's the default
+ # => it will be possible to update email with a duplicated value
+ validates_uniqueness_of :email, :on => :create
+
+ # => it will be possible to create the record with a 'non-numerical age'
+ validates_numericality_of :age, :on => :update
+
+ # => the default
+ validates_presence_of :name, :on => :save
end
------------------------------------------------------------------
@@ -367,7 +376,8 @@ Finally, it's possible to associate +:if+ and +:unless+ with a Ruby Proc object
[source, ruby]
------------------------------------------------------------------
class Account < ActiveRecord::Base
- validates_confirmation_of :password, :unless => Proc.new { |a| a.password.blank? }
+ validates_confirmation_of :password,
+ :unless => Proc.new { |a| a.password.blank? }
end
------------------------------------------------------------------
@@ -379,7 +389,8 @@ When the built-in validation helpers are not enough for your needs, you can writ
------------------------------------------------------------------
class Invoice < ActiveRecord::Base
def validate_on_create
- errors.add(:expiration_date, "can't be in the past") if !expiration_date.blank? and expiration_date < Date.today
+ errors.add(:expiration_date, "can't be in the past") if
+ !expiration_date.blank? and expiration_date < Date.today
end
end
------------------------------------------------------------------
@@ -389,14 +400,17 @@ If your validation rules are too complicated and you want to break them in small
[source, ruby]
------------------------------------------------------------------
class Invoice < ActiveRecord::Base
- validate :expiration_date_cannot_be_in_the_past, :discount_cannot_be_more_than_total_value
+ validate :expiration_date_cannot_be_in_the_past,
+ :discount_cannot_be_more_than_total_value
def expiration_date_cannot_be_in_the_past
- errors.add(:expiration_date, "can't be in the past") if !expiration_date.blank? and expiration_date < Date.today
+ errors.add(:expiration_date, "can't be in the past") if
+ !expiration_date.blank? and expiration_date < Date.today
end
def discount_cannot_be_greater_than_total_value
- errors.add(:discount, "can't be greater than total value") unless discount <= total_value
+ errors.add(:discount, "can't be greater than total value") unless
+ discount <= total_value
end
end
------------------------------------------------------------------
@@ -454,14 +468,16 @@ person.errors.on(:name) # => nil
person = Person.new(:name => "JD")
person.valid? # => false
-person.errors.on(:name) # => "is too short (minimum is 3 characters)"
+person.errors.on(:name)
+# => "is too short (minimum is 3 characters)"
person = Person.new
person.valid? # => false
-person.errors.on(:name) # => ["can't be blank", "is too short (minimum is 3 characters)"]
+person.errors.on(:name)
+# => ["can't be blank", "is too short (minimum is 3 characters)"]
------------------------------------------------------------------
-* +clear+ is used when you intentionally wants to clear all the messages in the +errors+ collection.
+* +clear+ is used when you intentionally want to clear all the messages in the +errors+ collection. However, calling +errors.clear+ upon an invalid object won't make it valid: the +errors+ collection will now be empty, but the next time you call +valid?+ or any method that tries to save this object to the database, the validations will run. If any of them fails, the +errors+ collection will get filled again.
[source, ruby]
------------------------------------------------------------------
@@ -471,10 +487,15 @@ class Person < ActiveRecord::Base
end
person = Person.new
-puts person.valid? # => false
-person.errors.on(:name) # => ["can't be blank", "is too short (minimum is 3 characters)"]
+person.valid? # => false
+person.errors.on(:name)
+# => ["can't be blank", "is too short (minimum is 3 characters)"]
+
person.errors.clear
-person.errors # => nil
+person.errors.empty? # => true
+p.save # => false
+p.errors.on(:name)
+# => ["can't be blank", "is too short (minimum is 3 characters)"]
------------------------------------------------------------------
== Callbacks
@@ -587,7 +608,7 @@ The +after_initialize+ and +after_find+ callbacks are a bit different from the o
== Halting Execution
-As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks and the database operation to be executed. However, if at any moment one of the callback methods returns a boolean +false+ (not +nil+) value, this execution chain will be halted and the desired operation will not complete: your model will not get persisted in the database, or your records will not get deleted and so on.
+As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks and the database operation to be executed. However, if at any moment one of the +before_create+, +before_save+, +before_update+ or +before_destroy+ callback methods returns a boolean +false+ (not +nil+) value, this execution chain will be halted and the desired operation will not complete: your model will not get persisted in the database, or your records will not get deleted and so on.
== Callback classes
@@ -667,7 +688,7 @@ end
=== Registering observers
-If you payed attention, you may be wondering where Active Record Observers are referenced in our applications, so they get instantiate and begin to interact with our models. For observers to work we need to register then in our application's *config/environment.rb* file. In this file there is a commented out line where we can define the observers that our application should load at start-up.
+If you payed attention, you may be wondering where Active Record Observers are referenced in our applications, so they get instantiate and begin to interact with our models. For observers to work we need to register them somewhere. The usual place to do that is in our application's *config/environment.rb* file. In this file there is a commented out line where we can define the observers that our application should load at start-up.
[source, ruby]
------------------------------------------------------------------
@@ -675,6 +696,10 @@ If you payed attention, you may be wondering where Active Record Observers are r
config.active_record.observers = :registration_observer, :auditor
------------------------------------------------------------------
+You can uncomment the line with +config.active_record.observers+ and change the symbols for the name of the observers that should be registered.
+
+It's also possible to register callbacks in any of the files living at *config/environments/*, if you want an observer to work only in a specific environment. There is not a +config.active_record.observers+ line at any of those files, but you can simply add it.
+
=== Where to put the observers' source files
By convention, you should always save your observers' source files inside *app/models*.
diff --git a/railties/doc/guides/source/command_line.txt b/railties/doc/guides/source/command_line.txt
index 1ad2e75c51..8a887bd001 100644
--- a/railties/doc/guides/source/command_line.txt
+++ b/railties/doc/guides/source/command_line.txt
@@ -52,7 +52,7 @@ NOTE: This output will seem very familiar when we get to the `generate` command.
=== server ===
-Let's try it! The `server` command launches a small web server written in Ruby named WEBrick which was also installed when you installed Rails. You'll use this any time you want to view your work through a web browser.
+Let's try it! The `server` command launches a small web server named WEBrick which comes bundled with Ruby. You'll use this any time you want to view your work through a web browser.
NOTE: WEBrick isn't your only option for serving Rails. We'll get to that in a later section. [XXX: which section]
@@ -99,7 +99,7 @@ Using generators will save you a large amount of time by writing *boilerplate co
Let's make our own controller with the controller generator. But what command should we use? Let's ask the generator:
-NOTE: All Rails console utilities have help text. For commands that require a lot of input to run correctly, you can try the command without any parameters (like `rails` or `./script/generate`). For others, you can try adding `--help` or `-h` to the end, as in `./script/server --help`.
+NOTE: All Rails console utilities have help text. As with most *NIX utilities, you can try adding `--help` or `-h` to the end, for example `./script/server --help`.
[source,shell]
------------------------------------------------------
@@ -200,24 +200,47 @@ Examples:
creates a Post model with a string title, text body, and published flag.
------------------------------------------------------
-Let's set up a simple model called "HighScore" that will keep track of our highest score on video games we play. Then we'll wire up our controller and view to modify and list our scores.
+But instead of generating a model directly (which we'll be doing later), let's set up a scaffold. A *scaffold* in Rails is a full set of model, database migration for that model, controller to manipulate it, views to view and manipulate the data, and a test suite for each of the above.
+
+Let's set up a simple resource called "HighScore" that will keep track of our highest score on video games we play.
[source,shell]
------------------------------------------------------
-$ ./script/generate model HighScore id:integer game:string score:integer
- exists app/models/
- exists test/unit/
- exists test/fixtures/
- create app/models/high_score.rb
- create test/unit/high_score_test.rb
- create test/fixtures/high_scores.yml
- create db/migrate
- create db/migrate/20081126032945_create_high_scores.rb
+$ ./script/generate scaffold HighScore game:string score:integer
+ exists app/models/
+ exists app/controllers/
+ exists app/helpers/
+ create app/views/high_scores
+ create app/views/layouts/
+ exists test/functional/
+ create test/unit/
+ create public/stylesheets/
+ create app/views/high_scores/index.html.erb
+ create app/views/high_scores/show.html.erb
+ create app/views/high_scores/new.html.erb
+ create app/views/high_scores/edit.html.erb
+ create app/views/layouts/high_scores.html.erb
+ create public/stylesheets/scaffold.css
+ create app/controllers/high_scores_controller.rb
+ create test/functional/high_scores_controller_test.rb
+ create app/helpers/high_scores_helper.rb
+ route map.resources :high_scores
+dependency model
+ exists app/models/
+ exists test/unit/
+ create test/fixtures/
+ create app/models/high_score.rb
+ create test/unit/high_score_test.rb
+ create test/fixtures/high_scores.yml
+ exists db/migrate
+ create db/migrate/20081217071914_create_high_scores.rb
------------------------------------------------------
-Taking it from the top, we have the *models* directory, where all of your data models live. *test/unit*, where all the unit tests live (gasp! -- unit tests!), fixtures for those tests, a test, the *migrate* directory, where the database-modifying migrations live, and a migration to create the `high_scores` table with the right fields.
+Taking it from the top - the generator checks that there exist the directories for models, controllers, helpers, layouts, functional and unit tests, stylesheets, creates the views, controller, model and database migration for HighScore (creating the `high_scores` table and fields), takes care of the route for the *resource*, and new tests for everything.
+
+The migration requires that we *migrate*, that is, run some Ruby code (living in that `20081217071914_create_high_scores.rb`) to modify the schema of our database. Which database? The sqlite3 database that Rails will create for you when we run the `rake db:migrate` command. We'll talk more about Rake in-depth in a little while.
-The migration requires that we *migrate*, that is, run some Ruby code (living in that `20081126032945_create_high_scores.rb`) to modify the schema of our database. Which database? The sqlite3 database that Rails will create for you when we run the `rake db:migrate` command. We'll talk more about Rake in-depth in a little while.
+NOTE: Hey. Install the sqlite3-ruby gem while you're at it. `gem install sqlite3-ruby`
[source,shell]
------------------------------------------------------
@@ -231,23 +254,87 @@ $ rake db:migrate
NOTE: Let's talk about unit tests. Unit tests are code that tests and makes assertions about code. In unit testing, we take a little part of code, say a method of a model, and test its inputs and outputs. Unit tests are your friend. The sooner you make peace with the fact that your quality of life will drastically increase when you unit test your code, the better. Seriously. We'll make one in a moment.
-Yo! Let's shove a small table into our greeting controller and view, listing our sweet scores.
+Let's see the interface Rails created for us. ./script/server; http://localhost:3000/high_scores
-[source,ruby]
+We can create new high scores (55,160 on Space Invaders!)
+
+=== console ===
+The `console` command lets you interact with your Rails application from the command line. On the underside, `script/console` uses IRB, so if you've ever used it, you'll be right at home. This is useful for testing out quick ideas with code and changing data server-side without touching the website.
+
+=== dbconsole ===
+`dbconsole` figures out which database you're using and drops you into whichever command line interface you would use with it (and figures out the command line parameters to give to it, too!). It supports MySQL, PostgreSQL, SQLite and SQLite3.
+
+=== plugin ===
+The `plugin` command simplifies plugin management; think a miniature version of the Gem utility. Let's walk through installing a plugin. You can call the sub-command *discover*, which sifts through repositories looking for plugins, or call *source* to add a specific repository of plugins, or you can specify the plugin location directly.
+
+Let's say you're creating a website for a client who wants a small accounting system. Every event having to do with money must be logged, and must never be deleted. Wouldn't it be great if we could override the behavior of a model to never actually take its record out of the database, but *instead*, just set a field?
+
+There is such a thing! The plugin we're installing is called "acts_as_paranoid", and it lets models implement a "deleted_at" column that gets set when you call destroy. Later, when calling find, the plugin will tack on a database check to filter out "deleted" things.
+
+[source,shell]
+------------------------------------------------------
+$ ./script/plugin install http://svn.techno-weenie.net/projects/plugins/acts_as_paranoid
++ ./CHANGELOG
++ ./MIT-LICENSE
+...
+...
------------------------------------------------------
-class GreetingController < ApplicationController
- def hello
- if request.post?
- score = HighScore.new(params[:high_score])
- if score.save
- flash[:notice] = "New score posted!"
- end
- end
-
- @scores = HighScore.find(:all)
- end
-end
+=== runner ===
+`runner` runs Ruby code in the context of Rails non-interactively. For instance:
+
+[source,shell]
------------------------------------------------------
+$ ./script/runner "Model.long_running_method"
+------------------------------------------------------
+
+=== destroy ===
+Think of `destroy` as the opposite of `generate`. It'll figure out what generate did, and undo it. Believe you-me, the creation of this tutorial used this command many times!
-XXX: Go with scaffolding instead, modifying greeting controller for high scores seems dumb.
+[source,shell]
+------------------------------------------------------
+$ ./script/generate model Oops
+ exists app/models/
+ exists test/unit/
+ exists test/fixtures/
+ create app/models/oops.rb
+ create test/unit/oops_test.rb
+ create test/fixtures/oops.yml
+ exists db/migrate
+ create db/migrate/20081221040817_create_oops.rb
+$ ./script/destroy model Oops
+ notempty db/migrate
+ notempty db
+ rm db/migrate/20081221040817_create_oops.rb
+ rm test/fixtures/oops.yml
+ rm test/unit/oops_test.rb
+ rm app/models/oops.rb
+ notempty test/fixtures
+ notempty test
+ notempty test/unit
+ notempty test
+ notempty app/models
+ notempty app
+------------------------------------------------------
+
+=== about ===
+Check it: Version numbers for Ruby, RubyGems, Rails, the Rails subcomponents, your application's folder, the current Rails environment name, your app's database adapter, and schema version! `about` is useful when you need to ask help, check if a security patch might affect you, or when you need some stats for an existing Rails installation.
+
+[source,shell]
+------------------------------------------------------
+$ ./script/about
+About your application's environment
+Ruby version 1.8.6 (i486-linux)
+RubyGems version 1.3.1
+Rails version 2.2.0
+Active Record version 2.2.0
+Action Pack version 2.2.0
+Active Resource version 2.2.0
+Action Mailer version 2.2.0
+Active Support version 2.2.0
+Edge Rails revision unknown
+Application root /home/commandsapp
+Environment development
+Database adapter sqlite3
+Database schema version 20081217073400
+------------------------------------------------------ \ No newline at end of file
diff --git a/railties/doc/guides/source/finders.txt b/railties/doc/guides/source/finders.txt
index 4c70c2b20b..88e7c15cb6 100644
--- a/railties/doc/guides/source/finders.txt
+++ b/railties/doc/guides/source/finders.txt
@@ -1,7 +1,7 @@
Rails Finders
=============
-This guide covers the +find+ method defined in +ActiveRecord::Base+, as well as other ways of finding particular instances of your models. By using this guide, you will be able to:
+This guide covers the +find+ method defined in ActiveRecord::Base, as well as other ways of finding particular instances of your models. By using this guide, you will be able to:
* Find records using a variety of methods and conditions
* Specify the order, retrieved attributes, grouping, and other properties of the found records
@@ -50,7 +50,7 @@ Active Record will perform queries on the database for you and is compatible wit
== IDs, First, Last and All
-+ActiveRecord::Base+ has methods defined on it to make interacting with your database and the tables within it much, much easier. For finding records, the key method is +find+. This method allows you to pass arguments into it to perform certain queries on your database without the need of SQL. If you wanted to find the record with the id of 1, you could type +Client.find(1)+ which would execute this query on your database:
+ActiveRecord::Base has methods defined on it to make interacting with your database and the tables within it much, much easier. For finding records, the key method is +find+. This method allows you to pass arguments into it to perform certain queries on your database without the need of SQL. If you wanted to find the record with the id of 1, you could type +Client.find(1)+ which would execute this query on your database:
[source, sql]
-------------------------------------------------------
@@ -74,9 +74,9 @@ SELECT * FROM clients WHERE (clients.id IN (1,2))
created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">]
-------------------------------------------------------
-Note that if you pass in a list of numbers that the result will be returned as an array, not as a single +Client+ object.
+Note that if you pass in a list of numbers that the result will be returned as an array, not as a single Client object.
-NOTE: If +find(id)+ or +find([id1, id2])+ fails to find any records, it will raise a +RecordNotFound+ exception.
+NOTE: If +find(id)+ or +find([id1, id2])+ fails to find any records, it will raise a RecordNotFound exception.
If you wanted to find the first Client object you would simply type +Client.first+ and that would find the first client in your clients table:
@@ -143,7 +143,7 @@ WARNING: Building your own conditions as pure strings can leave you vulnerable t
=== Array Conditions ===
-Now what if that number could vary, say as a parameter from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like +Client.first(:conditions => ["orders_count = ?", params[:orders]])+. Active Record will go through the first element in the conditions value and any additional elements will replace the question marks (?) in the first element. If you want to specify two conditions, you can do it like +Client.first(:conditions => ["orders_count = ? AND locked = ?", params[:orders], false])+. In this example, the first question mark will be replaced with the value in +params[:orders]+ and the second will be replaced with +false+ and this will find the first record in the table that has '2' as its value for the +orders_count+ field and +false+ for its locked field.
+Now what if that number could vary, say as a argument from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like +Client.first(:conditions => ["orders_count = ?", params[:orders]])+. Active Record will go through the first element in the conditions value and any additional elements will replace the question marks (?) in the first element. If you want to specify two conditions, you can do it like +Client.first(:conditions => ["orders_count = ? AND locked = ?", params[:orders], false])+. In this example, the first question mark will be replaced with the value in +params[:orders]+ and the second will be replaced with the SQL representation of +false+, which depends on the adapter.
The reason for doing code like:
@@ -159,7 +159,7 @@ instead of:
Client.first(:conditions => "orders_count = #{params[:orders]}")
-------------------------------------------------------
-is because of parameter safety. Putting the variable directly into the conditions string will pass the variable to the database *as-is*. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your parameters directly inside the conditions string.
+is because of argument safety. Putting the variable directly into the conditions string will pass the variable to the database *as-is*. This means that it will be an unescaped variable directly from a user who may have malicious intent. If you do this, you put your entire database at risk because once a user finds out he or she can exploit your database they can do just about anything to it. Never ever put your arguments directly inside the conditions string.
TIP: For more information on the dangers of SQL injection, see the link:../security.html#_sql_injection[Ruby on Rails Security Guide].
@@ -185,7 +185,7 @@ SELECT * FROM users WHERE (created_at IN
'2008-12-27','2008-12-28','2008-12-29','2008-12-30','2008-12-31'))
-------------------------------------------------------
-Things can get *really* messy if you pass in time objects as it will attempt to compare your field to *every second* in that range:
+Things can get *really* messy if you pass in Time objects as it will attempt to compare your field to *every second* in that range:
[source, ruby]
-------------------------------------------------------
@@ -224,7 +224,7 @@ Client.all(:conditions =>
["created_at >= ? AND created_at <= ?", params[:start_date], params[:end_date]])
-------------------------------------------------------
-Just like in Ruby.
+Just like in Ruby. If you want a shorter syntax be sure to check out the <<_hash_conditions, Hash Conditions>> section later on in the guide.
=== Placeholder Conditions ===
@@ -238,13 +238,83 @@ Client.all(:conditions =>
This makes for clearer readability if you have a large number of variable conditions.
+=== Hash Conditions
+
+Rails also allows you to pass in a hash conditions which can increase the readability of your conditions syntax. With hash conditions, you pass in a hash with keys of the fields you want conditionalised and the values of how you want to conditionalise them:
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:conditions => { :locked => true })
+-------------------------------------------------------
+
+The field name does not have to be a symbol it can also be a string:
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:conditions => { 'locked' => true })
+-------------------------------------------------------
+
+The good thing about this is that we can pass in a range for our fields without it generating a large query as shown in the preamble of this section.
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:conditions => { :created_at => (Time.now.midnight - 1.day)..Time.now.midnight})
+-------------------------------------------------------
+
+This will find all clients created yesterday by using a BETWEEN sql statement:
+
+[source, sql]
+-------------------------------------------------------
+SELECT * FROM `clients` WHERE (`clients`.`created_at` BETWEEN '2008-12-21 00:00:00' AND '2008-12-22 00:00:00')
+-------------------------------------------------------
+
+This demonstrates a shorter syntax for the examples in <<_array_conditions, Array Conditions>>
+
+You can also join in tables and specify their columns in the hash:
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:include => "orders", :conditions => { 'orders.created_at' => (Time.now.midnight - 1.day)..Time.now.midnight })
+-------------------------------------------------------
+
+An alternative and cleaner syntax to this is:
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:include => "orders", :conditions => { :orders => { :created_at => (Time.now.midnight - 1.day)..Time.now.midnight } })
+-------------------------------------------------------
+
+This will find all clients who have orders that were created yesterday, again using a BETWEEN expression.
+
+If you want to find records using the IN expression you can pass an array to the conditions hash:
+
+[source, ruby]
+-------------------------------------------------------
+Client.all(:include => "orders", :conditions => { :orders_count => [1,3,5] }
+-------------------------------------------------------
+
+This code will generate SQL like this:
+
+[source, sql]
+-------------------------------------------------------
+SELECT * FROM `clients` WHERE (`clients`.`orders_count` IN (1,2,3))
+-------------------------------------------------------
+
== Ordering
-If you're getting a set of records and want to force an order, you can use +Client.all(:order => "created_at")+ which by default will sort the records by ascending order. If you'd like to order it in descending order, just tell it to do that using +Client.all(:order => "created_at desc")+
+If you're getting a set of records and want to order them in ascending order by the +created_at+ field in your table, you can use +Client.all(:order => "created_at")+. If you'd like to order it in descending order, just tell it to do that using +Client.all(:order => "created_at desc")+. The value for this option is passed in as sanitized SQL and allows you to sort via multiple fields: +Client.all(:order => "created_at desc, orders_count asc")+.
== Selecting Certain Fields
-To select certain fields, you can use the select option like this: +Client.first(:select => "viewable_by, locked")+. This select option does not use an array of fields, but rather requires you to type SQL-like code. The above code will execute +SELECT viewable_by, locked FROM clients LIMIT 0,1+ on your database.
+To select certain fields, you can use the select option like this: +Client.first(:select => "viewable_by, locked")+. This select option does not use an array of fields, but rather requires you to type SQL-like code. The above code will execute +SELECT viewable_by, locked FROM clients LIMIT 1+ on your database.
+
+Be careful because this also means you're initializing a model object with only the fields that you've selected. If you attempt to access a field that is not in the initialized record you'll receive:
+
+-------------------------------------------------------
+ActiveRecord::MissingAttributeError: missing attribute: <attribute>
+-------------------------------------------------------
+
+Where <attribute> is the atrribute you asked for. The +id+ method will not raise the +ActiveRecord::MissingAttributeError+, so just be careful when working with associations because they need the +id+ method to function properly.
You can also call SQL functions within the select option. For example, if you would like to only grab a single record per unique value in a certain field by using the +DISTINCT+ function you can do it like this: +Client.all(:select => "DISTINCT(name)")+.
@@ -294,16 +364,29 @@ The SQL that would be executed would be something like this:
SELECT * FROM orders GROUP BY date(created_at)
-------------------------------------------------------
+== Having
+
+The +:having+ option allows you to specify SQL and acts as a kind of a filter on the group option. +:having+ can only be specified when +:group+ is specified.
+
+An example of using it would be:
+
+[source, ruby]
+-------------------------------------------------------
+Order.all(:group => "date(created_at)", :having => ["created_at > ?", 1.month.ago])
+-------------------------------------------------------
+
+This will return single order objects for each day, but only for the last month.
+
== Read Only
-Readonly is a find option that you can set in order to make that instance of the record read-only. Any attempt to alter or destroy the record will not succeed, raising an +Active Record::ReadOnlyRecord+ exception. To set this option, specify it like this:
++readonly+ is a +find+ option that you can set in order to make that instance of the record read-only. Any attempt to alter or destroy the record will not succeed, raising an ActiveRecord::ReadOnlyRecord exception. To set this option, specify it like this:
[source, ruby]
-------------------------------------------------------
Client.first(:readonly => true)
-------------------------------------------------------
-If you assign this record to a variable +client+, calling the following code will raise an +ActiveRecord::ReadOnlyRecord+ exception:
+If you assign this record to a variable client, calling the following code will raise an ActiveRecord::ReadOnlyRecord exception:
[source, ruby]
-------------------------------------------------------
@@ -324,13 +407,23 @@ Topic.transaction do
end
-------------------------------------------------------
+You can also pass SQL to this option to allow different types of locks. For example, MySQL has an expression called LOCK IN SHARE MODE where you can lock a record but still allow other queries to read it. To specify this expression just pass it in as the lock option:
+
+[source, ruby]
+-------------------------------------------------------
+Topic.transaction do
+ t = Topic.find(params[:id], :lock => "LOCK IN SHARE MODE")
+ t.increment!(:views)
+end
+-------------------------------------------------------
+
== Making It All Work Together
-You can chain these options together in no particular order as Active Record will write the correct SQL for you. If you specify two instances of the same options inside the find statement Active Record will use the latter.
+You can chain these options together in no particular order as Active Record will write the correct SQL for you. If you specify two instances of the same options inside the +find+ method Active Record will use the last one you specified. This is because the options passed to find are a hash and defining the same key twice in a hash will result in the last definition being used.
== Eager Loading
-Eager loading is loading associated records along with any number of records in as few queries as possible. For example, if you wanted to load all the addresses associated with all the clients in a single query you could use +Client.all(:include => :address)+. If you wanted to include both the address and mailing address for the client you would use +Client.find(:all, :include => [:address, :mailing_address]). Include will first find the client records and then load the associated address records. Running script/server in one window, and executing the code through script/console in another window, the output should look similar to this:
+Eager loading is loading associated records along with any number of records in as few queries as possible. For example, if you wanted to load all the addresses associated with all the clients in a single query you could use +Client.all(:include => :address)+. If you wanted to include both the address and mailing address for the client you would use +Client.find(:all, :include => [:address, :mailing_address])+. Include will first find the client records and then load the associated address records. Running script/server in one window, and executing the code through script/console in another window, the output should look similar to this:
[source, sql]
-------------------------------------------------------
@@ -341,9 +434,10 @@ MailingAddress Load (0.001985) SELECT mailing_addresses.* FROM
mailing_addresses WHERE (mailing_addresses.client_id IN (13,14))
-------------------------------------------------------
-The numbers +13+ and +14+ in the above SQL are the ids of the clients gathered from the +Client.all+ query. Rails will then run a query to gather all the addresses and mailing addresses that have a client_id of 13 or 14. Although this is done in 3 queries, this is more efficient than not eager loading because without eager loading it would run a query for every time you called +address+ or +mailing_address+ on one of the objects in the clients array, which may lead to performance issues if you're loading a large number of records at once.
+The numbers +13+ and +14+ in the above SQL are the ids of the clients gathered from the +Client.all+ query. Rails will then run a query to gather all the addresses and mailing addresses that have a client_id of 13 or 14. Although this is done in 3 queries, this is more efficient than not eager loading because without eager loading it would run a query for every time you called +address+ or +mailing_address+ on one of the objects in the clients array, which may lead to performance issues if you're loading a large number of records at once and is often called the "N+1 query problem". The problem is that the more queries your server has to execute, the slower it will run.
-If you wanted to get all the addresses for a client in the same query you would do +Client.all(:joins => :address)+ and you wanted to find the address and mailing address for that client you would do +Client.all(:joins => [:address, :mailing_address])+. This is more efficient because it does all the SQL in one query, as shown by this example:
+If you wanted to get all the addresses for a client in the same query you would do +Client.all(:joins => :address)+.
+If you wanted to find the address and mailing address for that client you would do +Client.all(:joins => [:address, :mailing_address])+. This is more efficient because it does all the SQL in one query, as shown by this example:
[source, sql]
-------------------------------------------------------
@@ -366,44 +460,44 @@ When using eager loading you can specify conditions for the columns of the table
[source, ruby]
-------------------------------------------------------
Client.first(:include => "orders", :conditions =>
- ["orders.created_at >= ? AND orders.created_at <= ?", Time.now - 2.weeks, Time.now])
+ ["orders.created_at >= ? AND orders.created_at <= ?", 2.weeks.ago, Time.now])
-------------------------------------------------------
== Dynamic finders
-For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +name+ on your Client model for example, you get +find_by_name+ and +find_all_by_name+ for free from Active Record. If you have also have a +locked+ field on the client model, you also get +find_by_locked+ and +find_all_by_locked+.
+For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +name+ on your Client model for example, you get +find_by_name+ and +find_all_by_name+ for free from Active Record. If you have also have a +locked+ field on the Client model, you also get +find_by_locked+ and +find_all_by_locked+.
-You can do +find_last_by_*+ methods too which will find the last record matching your parameter.
+You can do +find_last_by_*+ methods too which will find the last record matching your argument.
-You can specify an exclamation point (!) on the end of the dynamic finders to get them to raise an +ActiveRecord::RecordNotFound+ error if they do not return any records, like +Client.find_by_name!('Ryan')+
+You can specify an exclamation point (!) on the end of the dynamic finders to get them to raise an ActiveRecord::RecordNotFound error if they do not return any records, like +Client.find_by_name!("Ryan")+
-If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_name_and_locked('Ryan', true)+.
+If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_name_and_locked("Ryan", true)+.
-There's another set of dynamic finders that let you find or create/initialize objects if they aren't find. These work in a similar fashion to the other finders and can be used like +find_or_create_by_name(params[:name])+. Using this will firstly perform a find and then create if the find returns nil. The SQL looks like this for +Client.find_or_create_by_name('Ryan')+:
+There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_name(params[:name])+. Using this will firstly perform a find and then create if the find returns nil. The SQL looks like this for +Client.find_or_create_by_name("Ryan")+:
[source,sql]
-------------------------------------------------------
SELECT * FROM clients WHERE (clients.name = 'Ryan') LIMIT 1
BEGIN
INSERT INTO clients (name, updated_at, created_at, orders_count, locked)
- VALUES('Ryan', '2008-09-28 15:39:12', '2008-09-28 15:39:12', '0', '0')
+ VALUES('Ryan', '2008-09-28 15:39:12', '2008-09-28 15:39:12', 0, '0')
COMMIT
-------------------------------------------------------
-+find_or_create+'s sibling, +find_or_initialize+, will find an object and if it does not exist will act similar to calling +new+ with the parameters you passed in. For example:
++find_or_create+'s sibling, +find_or_initialize+, will find an object and if it does not exist will act similar to calling +new+ with the arguments you passed in. For example:
[source, ruby]
-------------------------------------------------------
client = Client.find_or_initialize_by_name('Ryan')
-------------------------------------------------------
-will either assign an existing client object with the name 'Ryan' to the client local variable, or initialize new object similar to calling +Client.new(:name => 'Ryan')+. From here, you can modify other fields in client by calling the attribute setters on it: +client.locked = true+ and when you want to write it to the database just call +save+ on it.
+will either assign an existing client object with the name 'Ryan' to the client local variable, or initialize a new object similar to calling +Client.new(:name => 'Ryan')+. From here, you can modify other fields in client by calling the attribute setters on it: +client.locked = true+ and when you want to write it to the database just call +save+ on it.
== Finding By SQL
-If you'd like to use your own SQL to find records a table you can use +find_by_sql+. The +find_by_sql+ method will return an array of objects even if it only returns a single record in it's call to the database. For example you could run this query:
+If you'd like to use your own SQL to find records in a table you can use +find_by_sql+. The +find_by_sql+ method will return an array of objects even the underlying query returns just a single record. For example you could run this query:
[source, ruby]
-------------------------------------------------------
@@ -423,7 +517,7 @@ Client.connection.select_all("SELECT * FROM `clients` WHERE `id` = '1'")
== Working with Associations
-When you define a has_many association on a model you get the find method and dynamic finders also on that association. This is helpful for finding associated records within the scope of an existing record, for example finding all the orders for a client that have been sent and not received by doing something like +Client.find(params[:id]).orders.find_by_sent_and_received(true, false)+. Having this find method available on associations is extremely helpful when using nested controllers.
+When you define a has_many association on a model you get the +find+ method and dynamic finders also on that association. This is helpful for finding associated records within the scope of an existing record, for example finding all the orders for a client that have been sent and not received by doing something like +Client.find(params[:id]).orders.find_by_sent_and_received(true, false)+. Having this find method available on associations is extremely helpful when using nested resources.
== Named Scopes
@@ -431,7 +525,7 @@ Named scopes are another way to add custom finding behavior to the models in the
=== Simple Named Scopes
-Suppose want to find all clients who are male. You could use this code:
+Suppose we want to find all clients who are male. You could use this code:
[source, ruby]
-------------------------------------------------------
@@ -451,7 +545,7 @@ class Client < ActiveRecord::Base
end
-------------------------------------------------------
-You can call this new named_scope with +Client.active.all+ and this will do the same query as if we just used +Client.all(:conditions => ["active = ?", true])+. Please be aware that the conditions syntax in named_scope and find is different and the two are not interchangeable. If you want to find the first client within this named scope you could do +Client.active.first+.
+You can call this new named_scope with +Client.active.all+ and this will do the same query as if we just used +Client.all(:conditions => ["active = ?", true])+. If you want to find the first client within this named scope you could do +Client.active.first+.
=== Combining Named Scopes
@@ -480,7 +574,7 @@ class Client < ActiveRecord::Base
end
-------------------------------------------------------
-This looks like a standard named scope that defines a method called recent which gathers all records created any time between now and 2 weeks ago. That's correct for the first time the model is loaded but for any time after that, +2.weeks.ago+ is set to that same value, so you will consistently get records from a certain date until your model is reloaded by something like your application restarting. The way to fix this is to put the code in a lambda block:
+This looks like a standard named scope that defines a method called +recent+ which gathers all records created any time between now and 2 weeks ago. That's correct for the first time the model is loaded but for any time after that, +2.weeks.ago+ is set to that same value, so you will consistently get records from a certain date until your model is reloaded by something like your application restarting. The way to fix this is to put the code in a lambda block:
[source, ruby]
-------------------------------------------------------
@@ -489,11 +583,11 @@ class Client < ActiveRecord::Base
end
-------------------------------------------------------
-And now every time the recent named scope is called, the code in the lambda block will be parsed, so you'll get actually 2 weeks ago from the code execution, not 2 weeks ago from the time the model was loaded.
+And now every time the +recent+ named scope is called, the code in the lambda block will be executed, so you'll get actually 2 weeks ago from the code execution, not 2 weeks ago from the time the model was loaded.
=== Named Scopes with Multiple Models
-In a named scope you can use +:include+ and +:joins+ options just like in find.
+In a named scope you can use +:include+ and +:joins+ options just like in +find+.
[source, ruby]
-------------------------------------------------------
@@ -507,7 +601,7 @@ This method, called as +Client.active_within_2_weeks.all+, will return all clien
=== Arguments to Named Scopes
-If you want to pass a named scope a compulsory argument, just specify it as a block parameter like this:
+If you want to pass to a named scope a required arugment, just specify it as a block argument like this:
[source, ruby]
-------------------------------------------------------
@@ -516,7 +610,7 @@ class Client < ActiveRecord::Base
end
-------------------------------------------------------
-This will work if you call +Client.recent(2.weeks.ago).all+ but not if you call +Client.recent+. If you want to add an optional argument for this, you have to use the splat operator as the block's parameter.
+This will work if you call +Client.recent(2.weeks.ago).all+ but not if you call +Client.recent+. If you want to add an optional argument for this, you have to use prefix the arugment with an *.
[source, ruby]
-------------------------------------------------------
@@ -553,14 +647,14 @@ Just like named scopes, anonymous scopes can be stacked, either with other anony
== Existence of Objects
-If you simply want to check for the existence of the object there's a method called +exists?+. This method will query the database using the same query as find, but instead of returning an object or collection of objects it will return either true or false.
+If you simply want to check for the existence of the object there's a method called +exists?+. This method will query the database using the same query as +find+, but instead of returning an object or collection of objects it will return either +true+ or false+.
[source, ruby]
-------------------------------------------------------
Client.exists?(1)
-------------------------------------------------------
-The above code will check for the existence of a clients table record with the id of 1 and return true if it exists.
+The +exists?+ method also takes multiple ids, but the catch is that it will return true if any one of those records exists.
[source, ruby]
-------------------------------------------------------
@@ -569,8 +663,6 @@ Client.exists?(1,2,3)
Client.exists?([1,2,3])
-------------------------------------------------------
-The +exists?+ method also takes multiple ids, as shown by the above code, but the catch is that it will return true if any one of those records exists.
-
Further more, +exists+ takes a +conditions+ option much like find:
[source, ruby]
@@ -593,10 +685,10 @@ Which will execute:
[source, sql]
-------------------------------------------------------
-SELECT count(*) AS count_all FROM clients WHERE (first_name = 1)
+SELECT count(*) AS count_all FROM clients WHERE (first_name = 'Ryan')
-------------------------------------------------------
-You can also use +include+ or +joins+ for this to do something a little more complex:
+You can also use +:include+ or +:joins+ for this to do something a little more complex:
[source, ruby]
-------------------------------------------------------
@@ -609,7 +701,7 @@ Which will execute:
-------------------------------------------------------
SELECT count(DISTINCT clients.id) AS count_all FROM clients
LEFT OUTER JOIN orders ON orders.client_id = client.id WHERE
- (clients.first_name = 'name' AND orders.status = 'received')
+ (clients.first_name = 'Ryan' AND orders.status = 'received')
-------------------------------------------------------
This code specifies +clients.first_name+ just in case one of the join tables has a field also called +first_name+ and it uses +orders.status+ because that's the name of our join table.
@@ -619,7 +711,7 @@ This code specifies +clients.first_name+ just in case one of the join tables has
If you want to see how many records are in your model's table you could call +Client.count+ and that will return the number. If you want to be more specific and find all the clients with their age present in the database you can use +Client.count(:age)+.
-For options, please see the parent section, Calculations.
+For options, please see the parent section, <<_calculations, Calculations>>.
=== Average
@@ -632,7 +724,7 @@ Client.average("orders_count")
This will return a number (possibly a floating point number such as 3.14159265) representing the average value in the field.
-For options, please see the parent section, <<_calculations, Calculations>>
+For options, please see the parent section, <<_calculations, Calculations>>.
=== Minimum
@@ -677,6 +769,14 @@ Thanks to Mike Gunderloy for his tips on creating this guide.
http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16[Lighthouse ticket]
+* December 23 2008: Xavier Noria suggestions added! From http://rails.lighthouseapp.com/projects/16213/tickets/16-activerecord-finders#ticket-16-27[this ticket] and http://rails.lighthouseapp.com/projects/16213/tickets/16-activerecord-finders#ticket-16-28[this ticket] and http://rails.lighthouseapp.com/projects/16213/tickets/16-activerecord-finders#ticket-16-29[this ticket]
+* December 22 2008: Added section on having.
+* December 22 2008: Added description of how to make hash conditions use an IN expression http://rails.loglibrary.com/chats/15279234[mentioned here]
+* December 22 2008: Mentioned using SQL as values for the lock option as mentioned in http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16-activerecord-finders#ticket-16-24[this ticket]
+* December 21 2008: Fixed http://rails.lighthouseapp.com/projects/16213/tickets/16-activerecord-finders#ticket-16-22[this ticket] minus two points; the lock SQL syntax and the having option.
+* December 21 2008: Added more to the has conditions section.
+* December 17 2008: Fixed up syntax errors.
+* December 16 2008: Covered hash conditions that were introduced in Rails 2.2.2.
* December 1 2008: Added using an SQL function example to Selecting Certain Fields section as per http://rails.lighthouseapp.com/projects/16213/tickets/36-adding-an-example-for-using-distinct-to-ar-finders[this ticket]
* November 23 2008: Added documentation for +find_by_last+ and +find_by_bang!+
* November 21 2008: Fixed all points specified in http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16-activerecord-finders#ticket-16-13[this comment] and http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16-activerecord-finders#ticket-16-14[this comment]
diff --git a/railties/doc/guides/source/form_helpers.txt b/railties/doc/guides/source/form_helpers.txt
index 88ca74a557..d09ad15a90 100644
--- a/railties/doc/guides/source/form_helpers.txt
+++ b/railties/doc/guides/source/form_helpers.txt
@@ -247,7 +247,7 @@ A nice thing about `f.text_field` and other helper methods is that they will pre
Relying on record identification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-In the previous chapter we handled the Article model. This model is directly available to users of our application and, following the best practices for developing with Rails, we should declare it *a resource*.
+In the previous chapter we handled the Article model. This model is directly available to users of our application, so -- following the best practices for developing with Rails -- we should declare it *a resource*.
When dealing with RESTful resources, our calls to `form_for` can get significantly easier if we rely on *record identification*. In short, we can just pass the model instance and have Rails figure out model name and the rest:
@@ -291,15 +291,13 @@ Here we have a list of cities where their names are presented to the user, but i
The select tag and options
~~~~~~~~~~~~~~~~~~~~~~~~~~
-The most generic helper is `select_tag`, which -- as the name implies -- simply generates the `SELECT` tag that encapsulates the options:
+The most generic helper is `select_tag`, which -- as the name implies -- simply generates the `SELECT` tag that encapsulates an options string:
----------------------------------------------------------------------------
<%= select_tag(:city_id, '<option value="1">Lisabon</option>...') %>
----------------------------------------------------------------------------
-This is a start, but it doesn't dynamically create our option tags. We had to pass them in as a string.
-
-We can generate option tags with the `options_for_select` helper:
+This is a start, but it doesn't dynamically create our option tags. We can generate option tags with the `options_for_select` helper:
----------------------------------------------------------------------------
<%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...]) %>
@@ -311,9 +309,9 @@ output:
...
----------------------------------------------------------------------------
-For input data we used a nested array where each element has two elements: visible value (name) and internal value (ID).
+For input data we used a nested array where each item has two elements: option text (city name) and option value (city id).
-Now you can combine `select_tag` and `options_for_select` to achieve the desired, complete markup:
+Knowing this, you can combine `select_tag` and `options_for_select` to achieve the desired, complete markup:
----------------------------------------------------------------------------
<%= select_tag(:city_id, options_for_select(...)) %>
@@ -333,13 +331,114 @@ output:
So whenever Rails sees that the internal value of an option being generated matches this value, it will add the `selected` attribute to that option.
-Select boxes for dealing with models
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Select helpers for dealing with models
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Until now we've covered how to make generic select boxes, but in most cases our form controls will be tied to a specific database model. So, to continue from our previous examples, let's assume that we have a "Person" model with a `city_id` attribute.
+Consistent with other form helpers, when dealing with models we drop the `"_tag"` suffix from `select_tag` that we used in previous examples:
+
----------------------------------------------------------------------------
-...
+# controller:
+@person = Person.new(:city_id => 2)
+
+# view:
+<%= select(:person, :city_id, [['Lisabon', 1], ['Madrid', 2], ...]) %>
+----------------------------------------------------------------------------
+
+Notice that the third parameter, the options array, is the same kind of argument we pass to `options_for_select`. One thing that we have as an advantage here is that we don't have to worry about pre-selecting the correct city if the user already has one -- Rails will do this for us by reading from `@person.city_id` attribute.
+
+As before, if we were to use `select` helper on a form builder scoped to `@person` object, the syntax would be:
+
+----------------------------------------------------------------------------
+# select on a form builder
+<%= f.select(:city_id, ...) %>
+----------------------------------------------------------------------------
+
+Option tags from a collection of arbitrary objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Until now we were generating option tags from nested arrays with the help of `options_for_select` method. Data in our array were raw values:
+
+----------------------------------------------------------------------------
+<%= options_for_select([['Lisabon', 1], ['Madrid', 2], ...]) %>
+----------------------------------------------------------------------------
+
+But what if we had a *City* model (perhaps an ActiveRecord one) and we wanted to generate option tags from a collection of those objects? One solution would be to make a nested array by iterating over them:
+
+----------------------------------------------------------------------------
+<% cities_array = City.find(:all).map { |city| [city.name, city.id] } %>
+<%= options_for_select(cities_array) %>
+----------------------------------------------------------------------------
+
+This is a perfectly valid solution, but Rails provides us with a less verbose alternative: `options_from_collection_for_select`. This helper expects a collection of arbitrary objects and two additional arguments: the names of the methods to read the option *value* and *text* from, respectively:
+
+----------------------------------------------------------------------------
+<%= options_from_collection_for_select(City.all, :id, :name) %>
+----------------------------------------------------------------------------
+
+As the name implies, this only generates option tags. A method to go along with it is `collection_select`:
+
+----------------------------------------------------------------------------
+<%= collection_select(:person, :city_id, City.all, :id, :name) %>
----------------------------------------------------------------------------
-... \ No newline at end of file
+To recap, `options_from_collection_for_select` are to `collection_select` what `options_for_select` are to `select`.
+
+Time zone and country select
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To leverage time zone support in Rails, we have to ask our users what time zone they are in. Doing so would require generating select options from a list of pre-defined TimeZone objects using `collection_select`, but we can simply use the `time_zone_select` helper that already wraps this:
+
+----------------------------------------------------------------------------
+<%= time_zone_select(:person, :city_id) %>
+----------------------------------------------------------------------------
+
+There is also `time_zone_options_for_select` helper for a more manual (therefore more customizable) way of doing this. Read the API documentation to learn about the possible arguments for these two methods.
+
+When it comes to country select, Rails _used_ to have the built-in helper `country_select` but was extracted to a plugin.
+TODO: plugin URL
+
+
+Miscellaneous
+-------------
+
+File upload form
+~~~~~~~~~~~~~~~~
+
+:multipart - If set to true, the enctype is set to "multipart/form-data".
+
+Scoping out form controls with `fields_for`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Creates a scope around a specific model object like `form_for`, but doesn’t create the form tags themselves. This makes `fields_for` suitable for specifying additional model objects in the same form:
+
+Making custom form builders
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can also build forms using a customized FormBuilder class. Subclass FormBuilder and override or define some more helpers, then use your custom builder. For example, let’s say you made a helper to automatically add labels to form inputs.
+
+----------------------------------------------------------------------------
+<% form_for :person, @person, :url => { :action => "update" }, :builder => LabellingFormBuilder do |f| %>
+ <%= f.text_field :first_name %>
+ <%= f.text_field :last_name %>
+ <%= text_area :person, :biography %>
+ <%= check_box_tag "person[admin]", @person.company.admin? %>
+<% end %>
+----------------------------------------------------------------------------
+
+
+* `form_for` within a namespace
+
+----------------------------------------------------------------------------
+select_tag(name, option_tags = nil, html_options = { :multiple, :disabled })
+
+select(object, method, choices, options = {}, html_options = {})
+options_for_select(container, selected = nil)
+
+collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
+options_from_collection_for_select(collection, value_method, text_method, selected = nil)
+
+time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
+time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
+----------------------------------------------------------------------------
diff --git a/railties/doc/guides/source/getting_started_with_rails.txt b/railties/doc/guides/source/getting_started_with_rails.txt
index b66d2f6f9e..58eff9fd3d 100644
--- a/railties/doc/guides/source/getting_started_with_rails.txt
+++ b/railties/doc/guides/source/getting_started_with_rails.txt
@@ -984,7 +984,7 @@ end
This creates +comments+ as a _nested resource_ within +posts+. This is another part of capturing the hierarchical relationship that exists between posts and comments.
-TIP: For more information on routing, see the link:../routing_outside_in[Rails Routing from the Outside In] guide.
+TIP: For more information on routing, see the link:../routing_outside_in.html[Rails Routing from the Outside In] guide.
=== Generating a Controller
diff --git a/railties/doc/guides/source/i18n.txt b/railties/doc/guides/source/i18n.txt
index 76f081e0bc..ba3cc42a5b 100644
--- a/railties/doc/guides/source/i18n.txt
+++ b/railties/doc/guides/source/i18n.txt
@@ -12,7 +12,7 @@ Internationalization is a complex problem. Natural languages differ in so many w
=== The overall architecture of the library
-To solve this the Ruby I18n gem is split into two parts:
+Thus, the Ruby I18n gem is split into two parts:
* The public API which is just a Ruby module with a bunch of public methods and definitions how the library works.
* A shipped backend (which is intentionally named the Simple backend) that implements these methods.
@@ -29,6 +29,14 @@ translate # lookup translations
localize # localize Date and Time objects to local formats
-------------------------------------------------------
+These have the aliases #t and #l so you can use them like this:
+
+[source, ruby]
+-------------------------------------------------------
+I18n.t 'store.title'
+I18n.l Time.now
+-------------------------------------------------------
+
There are also attribute readers and writers for the following attributes:
[source, ruby]
@@ -46,9 +54,30 @@ There are just a few, simple steps to get up and running with a I18n support for
=== Configure the I18n module
-First of all you want to tell the I18n library where it can find your custom translation files. You might also want to set your default locale to something else than English.
+Rails will wire up all required settings for you with sane defaults. If you need different settings you can overwrite them easily.
+
+The I18n library will use English (:en) as a *default locale* by default. I.e if you don't set a different locale, :en will be used for looking up translations. Also, Rails adds all files from config/locales/*.rb,yml to your translations load path.
+
+The *translations load path* (I18n.load_path) is just a Ruby Array of paths to your translation files that will be loaded automatically and available in your application. You can pick whatever directory and translation file naming scheme makes sense for you.
+
+(Hint: The backend will lazy-load these translations when a translation is looked up for the first time. This makes it possible to just swap the backend with something else even after translations have already been announced.)
-You can pick whatever directory and translation file naming scheme makes sense for you. The simplest thing possible is probably to put the following into an initializer:
+The default environment.rb says:
+
+[source, ruby]
+-------------------------------------------------------
+# The internationalization framework can be changed
+# to have another default locale (standard is :en) or more load paths.
+# All files from config/locales/*.rb,yml are added automatically.
+# config.i18n.load_path << Dir[File.join(RAILS_ROOT, 'my', 'locales', '*.{rb,yml}')]
+# config.i18n.default_locale = :de
+-------------------------------------------------------
+
+=== Optional: custom I18n configuration setup
+
+For the sake of completeness let's mention that if you do not want to use the environment for some reason you can always wire up things manually, too.
+
+To tell the I18n library where it can find your custom translation files you can specify the load path anywhere in your application - just make sure it gets run before any translations are actually looked up. You might also want to change the default locale. The simplest thing possible is to put the following into an initializer:
[source, ruby]
-------------------------------------------------------
@@ -58,14 +87,12 @@ You can pick whatever directory and translation file naming scheme makes sense f
I18n.load_path += Dir[ File.join(RAILS_ROOT, 'lib', 'locale', '*.{rb,yml}') ]
# you can omit this if you're happy with English as a default locale
-I18n.default_locale = :"pt"
+I18n.default_locale = :pt
-------------------------------------------------------
-I18n.load_path is just a Ruby Array of paths to your translation files. The backend will lazy-load these translations when a translation is looked up for the first time. This makes it possible to just swap the backend with something else even after translations have already been announced.
-
=== Set the locale in each request
-By default the I18n library will use the I18n.default_locale for looking up translations (if you do not specify a locale for a lookup) and this will, by default, en (English).
+By default the I18n library will use :en (English) as a I18n.default_locale for looking up translations (if you do not specify a locale for a lookup).
If you want to translate your Rails application to a single language other than English you can set I18n.default_locale to your locale. If you want to change the locale on a per-request basis though you can set it in a before_filter on the ApplicationController like this:
@@ -78,13 +105,15 @@ def set_locale
end
-------------------------------------------------------
-This will already work for URLs where you pass the locale as a query parameter as in example.com?locale=pt-BR (which is what Google also does). (TODO hints about other approaches in the resources section).
+This will already work for URLs where you pass the locale as a query parameter as in example.com?locale=pt (which is what Google also does).
+
+TIP: For other URL designs, see http://rails-i18n.org/wiki/pages/how-to-encode-the-current-locale-in-the-url[How to encode the current locale in the URL].
Now you've initialized I18n support for your application and told it which locale should be used. With that in place you're now ready for the really interesting stuff.
-=== Internationalize your application
+== Internationalize your application
-The process of "internationalization" usually means to abstract all strings and other locale specific bits out of your application (TODO reference to wikipedia). The process of "localization" means to then provide translations and localized formats for these bits.
+The process of "internationalization" usually means to abstract all strings and other locale specific bits out of your application. The process of "localization" means to then provide translations and localized formats for these bits. <<1>>
So, let's internationalize something. You most probably have something like this in one of your applications:
@@ -107,7 +136,9 @@ end
<p><%= flash[:notice] %></p>
-------------------------------------------------------
-TODO screenshot
+image:images/i18n/demo_untranslated.png[rails i18n demo untranslated]
+
+=== Adding Translations
Obviously there are two strings that are localized to English. In order to internationalize this code replace these strings with calls to Rails' #t helper with a key that makes sense for the translation:
@@ -125,39 +156,66 @@ end
<p><%= flash[:notice] %></p>
-------------------------------------------------------
-TODO insert note about #t helper compared to I18n.t
-
-TODO insert note/reference about structuring translation keys
-
When you now render this view it will show an error message that tells you that the translations for the keys :hello_world and :hello_flash are missing.
-TODO screenshot
+image:images/i18n/demo_translation_missing.png[rails i18n demo translation missing]
+
+NOTE: Rails adds a +t+ (+translate+) helper method to your views so that you do not need to spell out +I18n.t+ all the time. Additionally this helper will catch missing translations and wrap the resulting error message into a &lt;span class="translation_missing"&gt;.
So let's add the missing translations (i.e. do the "localization" part):
[source, ruby]
-------------------------------------------------------
-# lib/locale/en.yml
-en-US:
+# config/locale/en.yml
+en:
hello_world: Hello World
hello_flash: Hello Flash
-# lib/locale/pirate.yml
+# config/locale/pirate.yml
pirate:
hello_world: Ahoy World
hello_flash: Ahoy Flash
-------------------------------------------------------
-There you go. Your application now shows:
+There you go. Because you haven't changed the default_locale I18n will use English. Your application now shows:
+
+image:images/i18n/demo_translated_english.png[rails i18n demo translated to english]
-TODO screenshot
+And when you change the URL to pass the pirate locale you get:
+
+image:images/i18n/demo_translated_pirate.png[rails i18n demo translated to pirate]
+
+NOTE You need to restart the server when you add new locale files.
+
+=== Adding Date/Time formats
+
+Ok, let's add a timestamp to the view so we can demo the date/time localization feature as well. To localize the time format you pass the Time object to I18n.l or (preferably) use Rails' #l helper. You can pick a format by passing the :format option, by default the :default format is used.
[source, ruby]
-------------------------------------------------------
-I18n.t 'store.title'
-I18n.l Time.now
+# app/views/home/index.html.erb
+<h1><%=t :hello_world %></h1>
+<p><%= flash[:notice] %></p
+<p><%= l Time.now, :format => :short %></p>
+-------------------------------------------------------
+
+And in our pirate translations file let's add a time format (it's already there in Rails' defaults for English):
+
+[source, ruby]
+-------------------------------------------------------
+# config/locale/pirate.yml
+pirate:
+ time:
+ formats:
+ short: "arrrround %H'ish"
-------------------------------------------------------
+So that would give you:
+
+image:images/i18n/demo_localized_pirate.png[rails i18n demo localized time to pirate]
+
+NOTE Right now you might need to add some more date/time formats in order to make the I18n backend work as expected. See the http://github.com/svenfuchs/rails-i18n/tree/master/rails/locale[rails-i18n repository] for starting points.
+
== Overview of the I18n API features
@@ -218,7 +276,7 @@ I18n.t :missing, :default => 'Not here'
If the default value is a Symbol it will be used as a key and translated. One can provide multiple values as default. The first one that results in a value will be returned.
-E.g. the following first tries to translate the key :missing and then the key :also_missing. As both do not yield a result the string ‘Not here’ will be returned:
+E.g. the following first tries to translate the key :missing and then the key :also_missing. As both do not yield a result the string "Not here" will be returned:
[source, ruby]
-------------------------------------------------------
@@ -246,28 +304,29 @@ I18n.t 'active_record.error_messages'
=== Interpolation
-TODO explain what this is good for
+In many cases you want to abstract your translations so that variables can be interpolated into the translation. For this reason the I18n API provides an interpolation feature.
All options besides :default and :scope that are passed to #translate will be interpolated to the translation:
[source, ruby]
-------------------------------------------------------
-I18n.backend.store_translations 'en', :thanks => 'Thanks {{name}}!'
+I18n.backend.store_translations :en, :thanks => 'Thanks {{name}}!'
I18n.translate :thanks, :name => 'Jeremy'
# => 'Thanks Jeremy!'
-------------------------------------------------------
If a translation uses :default or :scope as a interpolation variable an I18n::ReservedInterpolationKey exception is raised. If a translation expects an interpolation variable but it has not been passed to #translate an I18n::MissingInterpolationArgument exception is raised.
+
=== Pluralization
-TODO explain what this is good for
+In English there's only a singular and a plural form for a given string, e.g. "1 message" and "2 messages". Other languages (http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html#ar[Arabic], http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html#ja[Japanese], http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html#ru[Russian] and many more) have different grammars that have additional or less http://www.unicode.org/cldr/data/charts/supplemental/language_plural_rules.html[plural forms]. Thus, the I18n API provides a flexible pluralization feature.
The :count interpolation variable has a special role in that it both is interpolated to the translation and used to pick a pluralization from the translations according to the pluralization rules defined by CLDR:
[source, ruby]
-------------------------------------------------------
-I18n.backend.store_translations 'en-US', :inbox => { # TODO change this
+I18n.backend.store_translations :en, :inbox => {
:one => '1 message',
:other => '{{count}} messages'
}
@@ -275,7 +334,7 @@ I18n.translate :inbox, :count => 2
# => '2 messages'
-------------------------------------------------------
-The algorithm for pluralizations in en-US is as simple as:
+The algorithm for pluralizations in :en is as simple as:
[source, ruby]
-------------------------------------------------------
@@ -294,7 +353,7 @@ If no locale is passed I18n.locale is used:
[source, ruby]
-------------------------------------------------------
-I18n.locale = :'de'
+I18n.locale = :de
I18n.t :foo
I18n.l Time.now
-------------------------------------------------------
@@ -303,27 +362,27 @@ Explicitely passing a locale:
[source, ruby]
-------------------------------------------------------
-I18n.t :foo, :locale => :'de'
-I18n.l Time.now, :locale => :'de'
+I18n.t :foo, :locale => :de
+I18n.l Time.now, :locale => :de
-------------------------------------------------------
-I18n.locale defaults to I18n.default_locale which defaults to :'en'. The default locale can be set like this:
+I18n.locale defaults to I18n.default_locale which defaults to :en. The default locale can be set like this:
[source, ruby]
-------------------------------------------------------
-I18n.default_locale = :'de'
+I18n.default_locale = :de
-------------------------------------------------------
== How to store your custom translations
-The shipped Simple backend allows you to store translations in both plain Ruby and YAML format. (2)
+The shipped Simple backend allows you to store translations in both plain Ruby and YAML format. <<2>>
For example a Ruby Hash providing translations can look like this:
[source, ruby]
-------------------------------------------------------
{
- :'pt-BR' => {
+ :pt => {
:foo => {
:bar => "baz"
}
@@ -335,18 +394,18 @@ The equivalent YAML file would look like this:
[source, ruby]
-------------------------------------------------------
-"pt-BR":
+pt:
foo:
bar: baz
-------------------------------------------------------
As you see in both cases the toplevel key is the locale. :foo is a namespace key and :bar is the key for the translation "baz".
-Here is a "real" example from the ActiveSupport en-US translations YAML file:
+Here is a "real" example from the ActiveSupport en.yml translations YAML file:
[source, ruby]
-------------------------------------------------------
-"en":
+en:
date:
formats:
default: "%Y-%m-%d"
@@ -364,12 +423,16 @@ I18n.t :short, :scope => 'date.formats'
I18n.t :short, :scope => [:date, :formats]
-------------------------------------------------------
+Generally we recommend using YAML as a format for storing translations. There are cases though where you want to store Ruby lambdas as part of your locale data, e.g. for special date
+
=== Translations for ActiveRecord models
You can use the methods Model.human_name and Model.human_attribute_name(attribute) to transparently lookup translations for your model and attribute names.
For example when you add the following translations:
+[source, ruby]
+-------------------------------------------------------
en:
activerecord:
models:
@@ -378,6 +441,7 @@ en:
user:
login: "Handle"
# will translate User attribute "login" as "Handle"
+-------------------------------------------------------
Then User.human_name will return "Dude" and User.human_attribute_name(:login) will return "Handle".
@@ -396,24 +460,21 @@ class User < ActiveRecord::Base
end
-------------------------------------------------------
-The key for the error message in this case is :blank. So ActiveRecord will first try to look up an error message with:
+The key for the error message in this case is :blank. ActiveRecord will lookup this key in the namespaces:
[source, ruby]
-------------------------------------------------------
-activerecord.errors.messages.models.user.attributes.name.blank
+activerecord.errors.messages.models.[model_name].attributes.[attribute_name]
+activerecord.errors.messages.models.[model_name]
+activerecord.errors.messages
-------------------------------------------------------
-If it's not there it will try:
+Thus, in our example it will try the following keys in this order and return the first result:
[source, ruby]
-------------------------------------------------------
+activerecord.errors.messages.models.user.attributes.name.blank
activerecord.errors.messages.models.user.blank
--------------------------------------------------------
-
-If this is also not there it will use the default message from:
-
-[source, ruby]
--------------------------------------------------------
activerecord.errors.messages.blank
-------------------------------------------------------
@@ -447,28 +508,27 @@ The translated model name and translated attribute name are always available for
So, for example, instead of the default error message "can not be blank" you could use the attribute name like this: "Please fill in your {{attribute}}".
-Count and/or value are available where applicable. Count can be used for pluralization if present:
-
-[grid="all"]
-`---------------------------`----------------`---------------`----------------
-validation with option message interpolation
-validates_confirmation_of - :confirmation -
-validates_acceptance_of - :accepted -
-validates_presence_of - :blank -
-validates_length_of :within, :in :too_short count
-validates_length_of :within, :in :too_long count
-validates_length_of :is :wrong_length count
-validates_length_of :minimum :too_short count
-validates_length_of :maximum :too_long count
-validates_uniqueness_of - :taken value
-validates_format_of - :invalid value
-validates_inclusion_of - :inclusion value
-validates_exclusion_of - :exclusion value
-validates_associated - :invalid value
-validates_numericality_of - :not_a_number value
-validates_numericality_of :odd :odd value
-validates_numericality_of :even :even value
-------------------------------------------------------------------------------
+count and/or value are available where applicable. Count can be used for pluralization if present:
+
+|==================================================================================
+| validation | with option | message | interpolation
+| validates_confirmation_of | - | :confirmation | -
+| validates_acceptance_of | - | :accepted | -
+| validates_presence_of | - | :blank | -
+| validates_length_of | :within, :in | :too_short | count
+| validates_length_of | :within, :in | :too_long | count
+| validates_length_of | :is | :wrong_length | count
+| validates_length_of | :minimum | :too_short | count
+| validates_length_of | :maximum | :too_long | count
+| validates_uniqueness_of | - | :taken | value
+| validates_format_of | - | :invalid | value
+| validates_inclusion_of | - | :inclusion | value
+| validates_exclusion_of | - | :exclusion | value
+| validates_associated | - | :invalid | value
+| validates_numericality_of | - | :not_a_number | value
+| validates_numericality_of | :odd | :odd | value
+| validates_numericality_of | :even | :even | value
+|==================================================================================
==== Translations for the ActiveRecord error_messages_for helper
@@ -479,14 +539,14 @@ Rails ships with the following translations:
[source, ruby]
-------------------------------------------------------
-"en":
+en:
activerecord:
errors:
template:
header:
one: "1 error prohibited this {{model}} from being saved"
other: "{{count}} errors prohibited this {{model}} from being saved"
- body: "There were problems with the following fields:"
+ body: "There were problems with the following fields:"
-------------------------------------------------------
@@ -496,11 +556,12 @@ Rails uses fixed strings and other localizations, such as format strings and oth
TODO list helpers and available keys
+
== Customize your I18n setup
=== Using different backends
-For several reasons the shipped Simple backend only does the "simplest thing that ever could work" _for Ruby on Rails_ (1) ... which means that it is only guaranteed to work for English and, as a side effect, languages that are very similar to English. Also, the simple backend is only capable of reading translations but can not dynamically store them to any format.
+For several reasons the shipped Simple backend only does the "simplest thing that ever could work" _for Ruby on Rails_ <<3>> ... which means that it is only guaranteed to work for English and, as a side effect, languages that are very similar to English. Also, the simple backend is only capable of reading translations but can not dynamically store them to any format.
That does not mean you're stuck with these limitations though. The Ruby I18n gem makes it very easy to exchange the Simple backend implementation with something else that fits better for your needs. E.g. you could exchange it with Globalize's Static backend:
@@ -509,30 +570,59 @@ That does not mean you're stuck with these limitations though. The Ruby I18n gem
I18n.backend = Globalize::Backend::Static.new
-------------------------------------------------------
-TODO expand this ...? list some backends and their features?
-
=== Using different exception handlers
-TODO
+The I18n API defines the following exceptions that will be raised by backends when the corresponding unexpected conditions occur:
-* Explain what exceptions are raised and why we are using exceptions for communication from backend to frontend.
-* Explain the default behaviour.
-* Explain the :raise option
+[source, ruby]
+-------------------------------------------------------
+MissingTranslationData # no translation was found for the requested key
+InvalidLocale # the locale set to I18n.locale is invalid (e.g. nil)
+InvalidPluralizationData # a count option was passed but the translation data is not suitable for pluralization
+MissingInterpolationArgument # the translation expects an interpolation argument that has not been passed
+ReservedInterpolationKey # the translation contains a reserved interpolation variable name (i.e. one of: scope, default)
+UnknownFileType # the backend does not know how to handle a file type that was added to I18n.load_path
+-------------------------------------------------------
+
+The I18n API will catch all of these exceptions when they were thrown in the backend and pass them to the default_exception_handler method. This method will re-raise all exceptions except for MissingTranslationData exceptions. When a MissingTranslationData exception has been caught it will return the exception’s error message string containing the missing key/scope.
+
+The reason for this is that during development you'd usually want your views to still render even though a translation is missing.
+
+In other contexts you might want to change this behaviour though. E.g. the default exception handling does not allow to catch missing translations during automated tests easily. For this purpose a different exception handler can be specified. The specified exception handler must be a method on the I18n module:
-* Example 1: the Rails #t helper uses a custom exception handler that catches I18n::MissingTranslationData and wraps the message into a span with the CSS class "translation_missing"
-* Example 2: for tests you might want a handler that just raises all exceptions all the time
-* Example 3: a handler
+[source, ruby]
+-------------------------------------------------------
+module I18n
+ def just_raise_that_exception(*args)
+ raise args.first
+ end
+end
+
+I18n.exception_handler = :just_raise_that_exception
+-------------------------------------------------------
+
+This would re-raise all caught exceptions including MissingTranslationData.
+
+Another example where the default behaviour is less desirable is the Rails TranslationHelper which provides the method #t (as well as #translate). When a MissingTranslationData exception occurs in this context the helper wraps the message into a span with the css class translation_missing.
+
+To do so the helper forces I18n#translate to raise exceptions no matter what exception handler is defined by setting the :raise option:
+
+[source, ruby]
+-------------------------------------------------------
+I18n.t :foo, :raise => true # always re-raises exceptions from the backend
+-------------------------------------------------------
== Resources
-* http://rails-i18n.org
== Footnotes
-(1) One of these reasons is that we don't want to any unnecessary load for applications that do not need any I18n capabilities, so we need to keep the I18n library as simple as possible for English. Another reason is that it is virtually impossible to implement a one-fits-all solution for all problems related to I18n for all existing languages. So a solution that allows us to exchange the entire implementation easily is appropriate anyway. This also makes it much easier to experiment with custom features and extensions.
+[[[1]]] Or, to quote http://en.wikipedia.org/wiki/Internationalization_and_localization[Wikipedia]: _"Internationalization is the process of designing a software application so that it can be adapted to various languages and regions without engineering changes. Localization is the process of adapting software for a specific region or language by adding locale-specific components and translating text."_
+
+[[[2]]] Other backends might allow or require to use other formats, e.g. a GetText backend might allow to read GetText files.
-(2) Other backends might allow or require to use other formats, e.g. a GetText backend might allow to read GetText files.
+[[[3]]] One of these reasons is that we don't want to any unnecessary load for applications that do not need any I18n capabilities, so we need to keep the I18n library as simple as possible for English. Another reason is that it is virtually impossible to implement a one-fits-all solution for all problems related to I18n for all existing languages. So a solution that allows us to exchange the entire implementation easily is appropriate anyway. This also makes it much easier to experiment with custom features and extensions.
== Credits
diff --git a/railties/doc/guides/source/images/i18n/demo_localized_pirate.png b/railties/doc/guides/source/images/i18n/demo_localized_pirate.png
new file mode 100644
index 0000000000..22b93416a0
--- /dev/null
+++ b/railties/doc/guides/source/images/i18n/demo_localized_pirate.png
Binary files differ
diff --git a/railties/doc/guides/source/images/i18n/demo_translated_en.png b/railties/doc/guides/source/images/i18n/demo_translated_en.png
new file mode 100644
index 0000000000..7ea0c437a5
--- /dev/null
+++ b/railties/doc/guides/source/images/i18n/demo_translated_en.png
Binary files differ
diff --git a/railties/doc/guides/source/images/i18n/demo_translated_pirate.png b/railties/doc/guides/source/images/i18n/demo_translated_pirate.png
new file mode 100644
index 0000000000..60ef370158
--- /dev/null
+++ b/railties/doc/guides/source/images/i18n/demo_translated_pirate.png
Binary files differ
diff --git a/railties/doc/guides/source/images/i18n/demo_translation_missing.png b/railties/doc/guides/source/images/i18n/demo_translation_missing.png
new file mode 100644
index 0000000000..86a3121cc1
--- /dev/null
+++ b/railties/doc/guides/source/images/i18n/demo_translation_missing.png
Binary files differ
diff --git a/railties/doc/guides/source/images/i18n/demo_untranslated.png b/railties/doc/guides/source/images/i18n/demo_untranslated.png
new file mode 100644
index 0000000000..e6717fb7d1
--- /dev/null
+++ b/railties/doc/guides/source/images/i18n/demo_untranslated.png
Binary files differ
diff --git a/railties/doc/guides/source/testing_rails_applications.txt b/railties/doc/guides/source/testing_rails_applications.txt
index b492fdb300..cb77829fc1 100644
--- a/railties/doc/guides/source/testing_rails_applications.txt
+++ b/railties/doc/guides/source/testing_rails_applications.txt
@@ -226,18 +226,18 @@ Above +rake db:migrate+ runs any pending migrations on the _developemnt_ environ
NOTE: +db:test:prepare+ will fail with an error if db/schema.rb doesn't exists.
-==== Rake Tasks for Preparing you Application for Testing ==
+==== Rake Tasks for Preparing your Application for Testing ====
[grid="all"]
-------------------------------------------------------------------------------------
-Tasks Description
-------------------------------------------------------------------------------------
-+rake db:test:clone+ Recreate the test database from the current environment's database schema
-+rake db:test:clone_structure+ Recreate the test databases from the development structure
-+rake db:test:load+ Recreate the test database from the current +schema.rb+
-+rake db:test:prepare+ Check for pending migrations and load the test schema
-+rake db:test:purge+ Empty the test database.
-------------------------------------------------------------------------------------
+|------------------------------------------------------------------------------------
+|Tasks Description
+|------------------------------------------------------------------------------------
+|+rake db:test:clone+ Recreate the test database from the current environment's database schema
+|+rake db:test:clone_structure+ Recreate the test databases from the development structure
+|+rake db:test:load+ Recreate the test database from the current +schema.rb+
+|+rake db:test:prepare+ Check for pending migrations and load the test schema
+|+rake db:test:purge+ Empty the test database.
+|------------------------------------------------------------------------------------
TIP: You can see all these rake tasks and their descriptions by running +rake \-\-tasks \-\-describe+
diff --git a/railties/lib/commands/about.rb b/railties/lib/commands/about.rb
index 7f53ac8a2e..bc2cfcb948 100644
--- a/railties/lib/commands/about.rb
+++ b/railties/lib/commands/about.rb
@@ -1,3 +1,3 @@
-require 'environment'
+require "#{RAILS_ROOT}/config/environment"
require 'rails/info'
puts Rails::Info
diff --git a/railties/lib/commands/dbconsole.rb b/railties/lib/commands/dbconsole.rb
index 6ff895aa30..06848d3c91 100644
--- a/railties/lib/commands/dbconsole.rb
+++ b/railties/lib/commands/dbconsole.rb
@@ -41,7 +41,7 @@ when "mysql"
if config['password'] && include_password
args << "--password=#{config['password']}"
- elsif config['password'] && !config['password'].empty?
+ elsif config['password'] && !config['password'].to_s.empty?
args << "-p"
end
diff --git a/railties/lib/commands/runner.rb b/railties/lib/commands/runner.rb
index 2411c3d270..510128318a 100644
--- a/railties/lib/commands/runner.rb
+++ b/railties/lib/commands/runner.rb
@@ -48,5 +48,7 @@ begin
eval(code_or_file)
end
ensure
- RAILS_DEFAULT_LOGGER.flush if RAILS_DEFAULT_LOGGER
+ if defined? Rails
+ Rails.logger.flush if Rails.logger.respond_to?(:flush)
+ end
end
diff --git a/railties/lib/commands/server.rb b/railties/lib/commands/server.rb
index 7057fcc33f..43b18004c0 100644
--- a/railties/lib/commands/server.rb
+++ b/railties/lib/commands/server.rb
@@ -84,7 +84,7 @@ else
end
app = Rack::Builder.new {
- use Rails::Rack::Logger
+ use Rails::Rack::LogTailer unless options[:detach]
use Rails::Rack::Static
use Rails::Rack::Debugger if options[:debugger]
run inner_app
diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb
index 06a3332c42..619701460d 100644
--- a/railties/lib/initializer.rb
+++ b/railties/lib/initializer.rb
@@ -39,7 +39,7 @@ module Rails
nil
end
end
-
+
def backtrace_cleaner
@@backtrace_cleaner ||= begin
# Relies on ActiveSupport, so we have to lazy load to postpone definition until AS has been loaded
@@ -148,7 +148,6 @@ module Rails
initialize_dependency_mechanism
initialize_whiny_nils
- initialize_temporary_session_directory
initialize_time_zone
initialize_i18n
@@ -156,6 +155,8 @@ module Rails
initialize_framework_settings
initialize_framework_views
+ initialize_metal
+
add_support_load_paths
load_gems
@@ -369,8 +370,9 @@ Run `rake gems:install` to install the missing gems.
def load_view_paths
if configuration.frameworks.include?(:action_view)
if configuration.cache_classes
- ActionController::Base.view_paths.load if configuration.frameworks.include?(:action_controller)
- ActionMailer::Base.template_root.load if configuration.frameworks.include?(:action_mailer)
+ view_path = ActionView::Template::EagerPath.new(configuration.view_path)
+ ActionController::Base.view_paths = view_path if configuration.frameworks.include?(:action_controller)
+ ActionMailer::Base.template_root = view_path if configuration.frameworks.include?(:action_mailer)
end
end
end
@@ -472,7 +474,7 @@ Run `rake gems:install` to install the missing gems.
# set to use Configuration#view_path.
def initialize_framework_views
if configuration.frameworks.include?(:action_view)
- view_path = ActionView::PathSet::Path.new(configuration.view_path, false)
+ view_path = ActionView::Template::Path.new(configuration.view_path)
ActionMailer::Base.template_root ||= view_path if configuration.frameworks.include?(:action_mailer)
ActionController::Base.view_paths = view_path if configuration.frameworks.include?(:action_controller) && ActionController::Base.view_paths.empty?
end
@@ -501,13 +503,6 @@ Run `rake gems:install` to install the missing gems.
require('active_support/whiny_nil') if configuration.whiny_nils
end
- def initialize_temporary_session_directory
- if configuration.frameworks.include?(:action_controller)
- session_path = "#{configuration.root_path}/tmp/sessions/"
- ActionController::Base.session_options[:tmpdir] = File.exist?(session_path) ? session_path : Dir::tmpdir
- end
- end
-
# Sets the default value for Time.zone, and turns on ActiveRecord::Base#time_zone_aware_attributes.
# If assigned value cannot be matched to a TimeZone, an exception will be raised.
def initialize_time_zone
@@ -529,7 +524,7 @@ Run `rake gems:install` to install the missing gems.
end
end
- # Set the i18n configuration from config.i18n but special-case for the load_path which should be
+ # Set the i18n configuration from config.i18n but special-case for the load_path which should be
# appended to what's already set instead of overwritten.
def initialize_i18n
configuration.i18n.each do |setting, value|
@@ -541,6 +536,10 @@ Run `rake gems:install` to install the missing gems.
end
end
+ def initialize_metal
+ configuration.middleware.insert_before(:"ActionController::VerbPiggybacking", Rails::Rack::Metal)
+ end
+
# Initializes framework-specific settings for each of the loaded frameworks
# (Configuration#frameworks). The available settings map to the accessors
# on each of the corresponding Base classes.
@@ -923,6 +922,7 @@ Run `rake gems:install` to install the missing gems.
# Followed by the standard includes.
paths.concat %w(
app
+ app/metal
app/models
app/controllers
app/helpers
@@ -941,6 +941,7 @@ Run `rake gems:install` to install the missing gems.
def default_eager_load_paths
%w(
+ app/metal
app/models
app/controllers
app/helpers
diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb
index f344c6477d..e1b422716d 100644
--- a/railties/lib/rails/backtrace_cleaner.rb
+++ b/railties/lib/rails/backtrace_cleaner.rb
@@ -2,21 +2,25 @@ module Rails
class BacktraceCleaner < ActiveSupport::BacktraceCleaner
ERB_METHOD_SIG = /:in `_run_erb_.*/
- VENDOR_DIRS = %w( vendor/plugins vendor/gems vendor/rails )
+ VENDOR_DIRS = %w( vendor/gems vendor/rails )
SERVER_DIRS = %w( lib/mongrel bin/mongrel
lib/passenger bin/passenger-spawn-server
lib/rack )
RAILS_NOISE = %w( script/server )
RUBY_NOISE = %w( rubygems/custom_require benchmark.rb )
+ GEMS_DIR = Gem.default_dir
+
ALL_NOISE = VENDOR_DIRS + SERVER_DIRS + RAILS_NOISE + RUBY_NOISE
def initialize
super
- add_filter { |line| line.sub(RAILS_ROOT, '') }
+ add_filter { |line| line.sub("#{RAILS_ROOT}/", '') }
add_filter { |line| line.sub(ERB_METHOD_SIG, '') }
add_filter { |line| line.sub('./', '/') } # for tests
+ add_filter { |line| line.sub(/(#{GEMS_DIR})\/gems\/([a-z]+)-([0-9.]+)\/(.*)/, '\2 (\3) \4')} # http://gist.github.com/30430
add_silencer { |line| ALL_NOISE.any? { |dir| line.include?(dir) } }
+ add_silencer { |line| line =~ %r(vendor/plugins/[^\/]+/lib) }
end
end
diff --git a/railties/lib/rails/plugin/locator.rb b/railties/lib/rails/plugin/locator.rb
index 678b295dc9..a6fc388a8e 100644
--- a/railties/lib/rails/plugin/locator.rb
+++ b/railties/lib/rails/plugin/locator.rb
@@ -30,7 +30,7 @@ module Rails
end
# The Rails::Plugin::FileSystemLocator will try to locate plugins by examining the directories
- # the the paths given in configuration.plugin_paths. Any plugins that can be found are returned
+ # in the paths given in configuration.plugin_paths. Any plugins that can be found are returned
# in a list.
#
# The criteria for a valid plugin in this case is found in Rails::Plugin#valid?, although
diff --git a/railties/lib/rails/rack.rb b/railties/lib/rails/rack.rb
index 90535674e9..9705f65e52 100644
--- a/railties/lib/rails/rack.rb
+++ b/railties/lib/rails/rack.rb
@@ -1,7 +1,8 @@
module Rails
module Rack
autoload :Debugger, "rails/rack/debugger"
- autoload :Logger, "rails/rack/logger"
+ autoload :LogTailer, "rails/rack/log_tailer"
+ autoload :Metal, "rails/rack/metal"
autoload :Static, "rails/rack/static"
end
end
diff --git a/railties/lib/rails/rack/log_tailer.rb b/railties/lib/rails/rack/log_tailer.rb
new file mode 100644
index 0000000000..a237cee6bc
--- /dev/null
+++ b/railties/lib/rails/rack/log_tailer.rb
@@ -0,0 +1,35 @@
+module Rails
+ module Rack
+ class LogTailer
+ EnvironmentLog = "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log"
+
+ def initialize(app, log = nil)
+ @app = app
+
+ path = Pathname.new(log || EnvironmentLog).cleanpath
+ @cursor = ::File.size(path)
+ @last_checked = Time.now.to_f
+
+ @file = ::File.open(path, 'r')
+ end
+
+ def call(env)
+ response = @app.call(env)
+ tail_log
+ response
+ end
+
+ def tail_log
+ @file.seek @cursor
+
+ mod = @file.mtime.to_f
+ if mod > @last_checked
+ contents = @file.read
+ @last_checked = mod
+ @cursor += contents.size
+ $stdout.print contents
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
deleted file mode 100644
index 89d02e45a9..0000000000
--- a/railties/lib/rails/rack/logger.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-module Rails
- module Rack
- class Logger
- EnvironmentLog = "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log"
-
- def initialize(app, log = nil)
- @app = app
- @path = Pathname.new(log || EnvironmentLog).cleanpath
- @cursor = ::File.size(@path)
- @last_checked = Time.now
- end
-
- def call(env)
- response = @app.call(env)
- ::File.open(@path, 'r') do |f|
- f.seek @cursor
- if f.mtime > @last_checked
- contents = f.read
- @last_checked = f.mtime
- @cursor += contents.length
- print contents
- end
- end
- response
- end
- end
- end
-end
diff --git a/railties/lib/rails/rack/metal.rb b/railties/lib/rails/rack/metal.rb
new file mode 100644
index 0000000000..b185227234
--- /dev/null
+++ b/railties/lib/rails/rack/metal.rb
@@ -0,0 +1,36 @@
+require 'active_support/ordered_hash'
+
+module Rails
+ module Rack
+ class Metal
+ NotFoundResponse = [404, {}, []].freeze
+ NotFound = lambda { NotFoundResponse }
+
+ def self.metals
+ base = "#{Rails.root}/app/metal"
+ matcher = /\A#{Regexp.escape(base)}\/(.*)\.rb\Z/
+
+ Dir["#{base}/**/*.rb"].sort.map do |file|
+ file.sub!(matcher, '\1')
+ require file
+ file.classify.constantize
+ end
+ end
+
+ def initialize(app)
+ @app = app
+ @metals = ActiveSupport::OrderedHash.new
+ self.class.metals.each { |app| @metals[app] = true }
+ freeze
+ end
+
+ def call(env)
+ @metals.keys.each do |app|
+ result = app.call(env)
+ return result unless result[0].to_i == 404
+ end
+ @app.call(env)
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/commands.rb b/railties/lib/rails_generator/commands.rb
index cacb3807d6..299044c3d7 100644
--- a/railties/lib/rails_generator/commands.rb
+++ b/railties/lib/rails_generator/commands.rb
@@ -294,7 +294,7 @@ HELP
file(relative_source, relative_destination, template_options) do |file|
# Evaluate any assignments in a temporary, throwaway binding.
vars = template_options[:assigns] || {}
- b = binding
+ b = template_options[:binding] || binding
vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
# Render the source file with the temporary binding.
diff --git a/railties/lib/rails_generator/generators/applications/app/template_runner.rb b/railties/lib/rails_generator/generators/applications/app/template_runner.rb
index c6113648e6..bb7bd0e6f4 100644
--- a/railties/lib/rails_generator/generators/applications/app/template_runner.rb
+++ b/railties/lib/rails_generator/generators/applications/app/template_runner.rb
@@ -8,23 +8,24 @@ require 'fileutils'
module Rails
class TemplateRunner
attr_reader :root
+ attr_writer :logger
def initialize(template, root = '') # :nodoc:
- @root = File.join(Dir.pwd, root)
+ @root = File.expand_path(File.directory?(root) ? root : File.join(Dir.pwd, root))
- puts "applying template: #{template}"
+ log 'applying', "template: #{template}"
load_template(template)
- puts "#{template} applied."
+ log 'applied', "#{template}"
end
def load_template(template)
begin
code = open(template).read
in_root { self.instance_eval(code) }
- rescue LoadError
- raise "The template [#{template}] could not be loaded."
+ rescue LoadError, Errno::ENOENT => e
+ raise "The template [#{template}] could not be loaded. Error: #{e}"
end
end
@@ -41,8 +42,8 @@ module Rails
#
# file("config/apach.conf", "your apache config")
#
- def file(filename, data = nil, &block)
- puts "creating file #{filename}"
+ def file(filename, data = nil, log_action = true, &block)
+ log 'file', filename if log_action
dir, file = [File.dirname(filename), File.basename(filename)]
inside(dir) do
@@ -66,7 +67,7 @@ module Rails
# plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk'
#
def plugin(name, options)
- puts "installing plugin #{name}"
+ log 'plugin', name
if options[:git] && options[:submodule]
in_root do
@@ -74,28 +75,36 @@ module Rails
end
elsif options[:git] || options[:svn]
in_root do
- `script/plugin install #{options[:svn] || options[:git]}`
+ run("script/plugin install #{options[:svn] || options[:git]}", false)
end
else
- puts "! no git or svn provided for #{name}. skipping..."
+ log "! no git or svn provided for #{name}. skipping..."
end
end
# Adds an entry into config/environment.rb for the supplied gem :
def gem(name, options = {})
- puts "adding gem #{name}"
+ log 'gem', name
- sentinel = 'Rails::Initializer.run do |config|'
gems_code = "config.gem '#{name}'"
if options.any?
- opts = options.inject([]) {|result, h| result << [":#{h[0]} => '#{h[1]}'"] }.join(", ")
+ opts = options.inject([]) {|result, h| result << [":#{h[0]} => '#{h[1]}'"] }.sort.join(", ")
gems_code << ", #{opts}"
end
+ environment gems_code
+ end
+
+ # Adds a line inside the Initializer block for config/environment.rb. Used by #gem
+ def environment(data = nil, &block)
+ sentinel = 'Rails::Initializer.run do |config|'
+
+ data = block.call if !data && block_given?
+
in_root do
gsub_file 'config/environment.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
- "#{match}\n #{gems_code}"
+ "#{match}\n " << data
end
end
end
@@ -111,11 +120,11 @@ module Rails
def git(command = {})
in_root do
if command.is_a?(Symbol)
- puts "running git #{command}"
+ log 'running', "git #{command}"
Git.run(command.to_s)
else
command.each do |command, options|
- puts "running git #{command} #{options}"
+ log 'running', "git #{command} #{options}"
Git.run("#{command} #{options}")
end
end
@@ -135,16 +144,8 @@ module Rails
# vendor("foreign.rb", "# Foreign code is fun")
#
def vendor(filename, data = nil, &block)
- puts "vendoring file #{filename}"
- inside("vendor") do |folder|
- File.open("#{folder}/#{filename}", "w") do |f|
- if block_given?
- f.write(block.call)
- else
- f.write(data)
- end
- end
- end
+ log 'vendoring', filename
+ file("vendor/#{filename}", data, false, &block)
end
# Create a new file in the lib/ directory. Code can be specified
@@ -158,17 +159,9 @@ module Rails
#
# lib("foreign.rb", "# Foreign code is fun")
#
- def lib(filename, data = nil)
- puts "add lib file #{filename}"
- inside("lib") do |folder|
- File.open("#{folder}/#{filename}", "w") do |f|
- if block_given?
- f.write(block.call)
- else
- f.write(data)
- end
- end
- end
+ def lib(filename, data = nil, &block)
+ log 'lib', filename
+ file("lib/#{filename}", data, false, &block)
end
# Create a new Rakefile with the provided code (either in a block or a string).
@@ -190,16 +183,8 @@ module Rails
# rakefile("seed.rake", "puts 'im plantin ur seedz'")
#
def rakefile(filename, data = nil, &block)
- puts "adding rakefile #{filename}"
- inside("lib/tasks") do |folder|
- File.open("#{folder}/#{filename}", "w") do |f|
- if block_given?
- f.write(block.call)
- else
- f.write(data)
- end
- end
- end
+ log 'rakefile', filename
+ file("lib/tasks/#{filename}", data, false, &block)
end
# Create a new initializer with the provided code (either in a block or a string).
@@ -219,16 +204,8 @@ module Rails
# initializer("api.rb", "API_KEY = '123456'")
#
def initializer(filename, data = nil, &block)
- puts "adding initializer #{filename}"
- inside("config/initializers") do |folder|
- File.open("#{folder}/#{filename}", "w") do |f|
- if block_given?
- f.write(block.call)
- else
- f.write(data)
- end
- end
- end
+ log 'initializer', filename
+ file("config/initializers/#{filename}", data, false, &block)
end
# Generate something using a generator from Rails or a plugin.
@@ -240,10 +217,10 @@ module Rails
# generate(:authenticated, "user session")
#
def generate(what, *args)
- puts "generating #{what}"
+ log 'generating', what
argument = args.map(&:to_s).flatten.join(" ")
- in_root { `#{root}/script/generate #{what} #{argument}` }
+ in_root { run("script/generate #{what} #{argument}", false) }
end
# Executes a command
@@ -254,8 +231,8 @@ module Rails
# run('ln -s ~/edge rails)
# end
#
- def run(command)
- puts "executing #{command} from #{Dir.pwd}"
+ def run(command, log_action = true)
+ log 'executing', "#{command} from #{Dir.pwd}" if log_action
`#{command}`
end
@@ -268,10 +245,10 @@ module Rails
# rake("gems:install", :sudo => true)
#
def rake(command, options = {})
- puts "running rake task #{command}"
+ log 'rake', command
env = options[:env] || 'development'
sudo = options[:sudo] ? 'sudo ' : ''
- in_root { `#{sudo}rake #{command} RAILS_ENV=#{env}` }
+ in_root { run("#{sudo}rake #{command} RAILS_ENV=#{env}", false) }
end
# Just run the capify command in root
@@ -281,7 +258,8 @@ module Rails
# capify!
#
def capify!
- in_root { `capify .` }
+ log 'capifying'
+ in_root { run('capify .', false) }
end
# Add Rails to /vendor/rails
@@ -291,8 +269,8 @@ module Rails
# freeze!
#
def freeze!(args = {})
- puts "vendoring rails edge"
- in_root { `rake rails:freeze:edge` }
+ log 'vendor', 'rails edge'
+ in_root { run('rake rails:freeze:edge', false) }
end
# Make an entry in Rails routing file conifg/routes.rb
@@ -302,6 +280,7 @@ module Rails
# route "map.root :controller => :welcome"
#
def route(routing_code)
+ log 'route', routing_code
sentinel = 'ActionController::Routing::Routes.draw do |map|'
in_root do
@@ -321,7 +300,7 @@ module Rails
# freeze! if ask("Should I freeze the latest Rails?") == "yes"
#
def ask(string)
- puts string
+ log '', string
gets.strip
end
@@ -368,5 +347,23 @@ module Rails
def destination_path(relative_destination)
File.join(root, relative_destination)
end
+
+ def log(action, message = '')
+ logger.log(action, message)
+ end
+
+ def logger
+ @logger ||= Rails::Generator::Base.logger
+ end
+
+ def logger
+ @logger ||= if defined?(Rails::Generator::Base)
+ Rails::Generator::Base.logger
+ else
+ require 'rails_generator/simple_logger'
+ Rails::Generator::SimpleLogger.new(STDOUT)
+ end
+ end
+
end
end \ No newline at end of file
diff --git a/railties/lib/rails_generator/generators/components/metal/USAGE b/railties/lib/rails_generator/generators/components/metal/USAGE
new file mode 100644
index 0000000000..123ec6c03f
--- /dev/null
+++ b/railties/lib/rails_generator/generators/components/metal/USAGE
@@ -0,0 +1,8 @@
+Description:
+ Cast some metal!
+
+Examples:
+ `./script/generate metal poller`
+
+ This will create:
+ Metal: app/metal/poller.rb
diff --git a/railties/lib/rails_generator/generators/components/metal/metal_generator.rb b/railties/lib/rails_generator/generators/components/metal/metal_generator.rb
new file mode 100644
index 0000000000..64f49d929d
--- /dev/null
+++ b/railties/lib/rails_generator/generators/components/metal/metal_generator.rb
@@ -0,0 +1,8 @@
+class MetalGenerator < Rails::Generator::NamedBase
+ def manifest
+ record do |m|
+ m.directory 'app/metal'
+ m.template 'metal.rb', File.join('app/metal', "#{file_name}.rb")
+ end
+ end
+end
diff --git a/railties/lib/rails_generator/generators/components/metal/templates/metal.rb b/railties/lib/rails_generator/generators/components/metal/templates/metal.rb
new file mode 100644
index 0000000000..e94982b69a
--- /dev/null
+++ b/railties/lib/rails_generator/generators/components/metal/templates/metal.rb
@@ -0,0 +1,12 @@
+# Allow the metal piece to run in isolation
+require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)
+
+class <%= class_name %>
+ def self.call(env)
+ if env["PATH_INFO"] =~ /^\/<%= file_name %>/
+ [200, {"Content-Type" => "text/html"}, ["Hello, World!"]]
+ else
+ [404, {"Content-Type" => "text/html"}, ["Not Found"]]
+ end
+ end
+end
diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake
index 3a576063fa..68ffefae0b 100644
--- a/railties/lib/tasks/databases.rake
+++ b/railties/lib/tasks/databases.rake
@@ -380,7 +380,7 @@ namespace :db do
end
namespace :sessions do
- desc "Creates a sessions migration for use with CGI::Session::ActiveRecordStore"
+ desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
task :create => :environment do
raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations?
require 'rails_generator'
diff --git a/railties/lib/tasks/middleware.rake b/railties/lib/tasks/middleware.rake
index e0dcf50307..05f159184e 100644
--- a/railties/lib/tasks/middleware.rake
+++ b/railties/lib/tasks/middleware.rake
@@ -1,6 +1,6 @@
desc 'Prints out your Rack middleware stack'
task :middleware => :environment do
- ActionController::Dispatcher.middleware.each do |middleware|
+ ActionController::Dispatcher.middleware.active.each do |middleware|
puts "use #{middleware.inspect}"
end
puts "run ActionController::Dispatcher.new"
diff --git a/railties/lib/tasks/tmp.rake b/railties/lib/tasks/tmp.rake
index b191039d63..fea15058bb 100644
--- a/railties/lib/tasks/tmp.rake
+++ b/railties/lib/tasks/tmp.rake
@@ -2,7 +2,7 @@ namespace :tmp do
desc "Clear session, cache, and socket files from tmp/"
task :clear => [ "tmp:sessions:clear", "tmp:cache:clear", "tmp:sockets:clear"]
- desc "Creates tmp directories for sessions, cache, and sockets"
+ 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 ))
end
@@ -34,4 +34,4 @@ namespace :tmp do
FileUtils.rm(Dir['tmp/pids/[^.]*'])
end
end
-end \ No newline at end of file
+end
diff --git a/railties/lib/test_help.rb b/railties/lib/test_help.rb
index b5c92c1790..ee24ea3a45 100644
--- a/railties/lib/test_help.rb
+++ b/railties/lib/test_help.rb
@@ -3,7 +3,6 @@
silence_warnings { RAILS_ENV = "test" }
require 'test/unit'
-require 'active_support/test_case'
require 'action_controller/test_case'
require 'action_view/test_case'
require 'action_controller/integration'
diff --git a/railties/test/console_app_test.rb b/railties/test/console_app_test.rb
index 6cfc907b80..f419fe0d8d 100644
--- a/railties/test/console_app_test.rb
+++ b/railties/test/console_app_test.rb
@@ -4,6 +4,7 @@ require 'action_controller' # console_app uses 'action_controller/integration'
unless defined? ApplicationController
class ApplicationController < ActionController::Base; end
+ ActionController::Base.session_store = nil
end
require 'dispatcher'
@@ -13,6 +14,15 @@ require 'console_app'
Test::Unit.run = false
class ConsoleAppTest < Test::Unit::TestCase
+ def test_app_method_should_return_integration_session
+ assert_nothing_thrown do
+ console_session = app
+ assert_not_nil console_session
+ assert_instance_of ActionController::Integration::Session,
+ console_session
+ end
+ end
+
uses_mocha 'console reload test' do
def test_reload_should_fire_preparation_callbacks
a = b = c = nil
diff --git a/railties/test/error_page_test.rb b/railties/test/error_page_test.rb
index 844f889aad..f819e468e8 100644
--- a/railties/test/error_page_test.rb
+++ b/railties/test/error_page_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
require 'action_controller'
-require 'action_controller/test_process'
+require 'action_controller/test_case'
RAILS_ENV = "test"
CURRENT_DIR = File.expand_path(File.dirname(__FILE__))
@@ -22,13 +22,10 @@ ActionController::Routing::Routes.draw do |map|
map.connect ':controller/:action/:id'
end
-class ErrorPageControllerTest < Test::Unit::TestCase
+class ErrorPageControllerTest < ActionController::TestCase
def setup
- @controller = ErrorPageController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
-
ActionController::Base.consider_all_requests_local = false
+ rescue_action_in_public!
end
def test_500_error_page_instructs_system_administrator_to_check_log_file
@@ -38,6 +35,6 @@ class ErrorPageControllerTest < Test::Unit::TestCase
end
get :crash
expected_log_file = "#{RAILS_ENV}.log"
- assert_not_nil @response.body.index(expected_log_file)
+ assert_not_nil @response.body.index(expected_log_file), @response.body
end
end
diff --git a/railties/test/fcgi_dispatcher_test.rb b/railties/test/fcgi_dispatcher_test.rb
index cc054c24aa..c469c5dd01 100644
--- a/railties/test/fcgi_dispatcher_test.rb
+++ b/railties/test/fcgi_dispatcher_test.rb
@@ -1,10 +1,9 @@
require 'abstract_unit'
begin
+require 'action_controller'
require 'fcgi_handler'
-module ActionController; module Routing; module Routes; end end end
-
class RailsFCGIHandlerTest < Test::Unit::TestCase
def setup
@log = StringIO.new
@@ -131,19 +130,11 @@ class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase
end
end
- class ::Dispatcher
- class << self
- attr_accessor :signal
- alias_method :old_dispatch, :dispatch
- def dispatch(cgi)
- signal ? Process.kill(signal, $$) : old_dispatch
- end
- end
- end
-
def setup
@log = StringIO.new
@handler = RailsFCGIHandler.new(@log)
+ @dispatcher = mock
+ Dispatcher.stubs(:new).returns(@dispatcher)
end
def test_interrupted_via_HUP_when_not_in_request
@@ -159,19 +150,6 @@ class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase
assert_equal :reload, @handler.when_ready
end
- def test_interrupted_via_HUP_when_in_request
- cgi = mock
- FCGI.expects(:each_cgi).once.yields(cgi)
- Dispatcher.expects(:signal).times(2).returns('HUP')
-
- @handler.expects(:reload!).once
- @handler.expects(:close_connection).never
- @handler.expects(:exit).never
-
- @handler.process!
- assert_equal :reload, @handler.when_ready
- end
-
def test_interrupted_via_USR1_when_not_in_request
cgi = mock
FCGI.expects(:each_cgi).once.yields(cgi)
@@ -186,19 +164,6 @@ class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase
assert_nil @handler.when_ready
end
- def test_interrupted_via_USR1_when_in_request
- cgi = mock
- FCGI.expects(:each_cgi).once.yields(cgi)
- Dispatcher.expects(:signal).times(2).returns('USR1')
-
- @handler.expects(:reload!).never
- @handler.expects(:close_connection).with(cgi).once
- @handler.expects(:exit).never
-
- @handler.process!
- assert_equal :exit, @handler.when_ready
- end
-
def test_restart_via_USR2_when_in_request
cgi = mock
FCGI.expects(:each_cgi).once.yields(cgi)
@@ -217,7 +182,7 @@ class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase
def test_interrupted_via_TERM
cgi = mock
FCGI.expects(:each_cgi).once.yields(cgi)
- Dispatcher.expects(:signal).times(2).returns('TERM')
+ ::Rack::Handler::FastCGI.expects(:serve).once.returns('TERM')
@handler.expects(:reload!).never
@handler.expects(:close_connection).never
@@ -238,7 +203,7 @@ class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase
cgi = mock
error = RuntimeError.new('foo')
FCGI.expects(:each_cgi).once.yields(cgi)
- Dispatcher.expects(:dispatch).once.with(cgi).raises(error)
+ ::Rack::Handler::FastCGI.expects(:serve).once.raises(error)
@handler.expects(:dispatcher_error).with(error, regexp_matches(/^unhandled/))
@handler.process!
end
@@ -254,7 +219,7 @@ class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase
cgi = mock
error = SignalException.new('USR2')
FCGI.expects(:each_cgi).once.yields(cgi)
- Dispatcher.expects(:dispatch).once.with(cgi).raises(error)
+ ::Rack::Handler::FastCGI.expects(:serve).once.raises(error)
@handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/))
@handler.process!
end
@@ -284,7 +249,7 @@ class RailsFCGIHandlerPeriodicGCTest < Test::Unit::TestCase
cgi = mock
FCGI.expects(:each_cgi).times(10).yields(cgi)
- Dispatcher.expects(:dispatch).times(10).with(cgi)
+ Dispatcher.expects(:new).times(10)
@handler.expects(:run_gc!).never
9.times { @handler.process! }
diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb
index 1d4f2b18b3..6c1f0961a1 100644
--- a/railties/test/gem_dependency_test.rb
+++ b/railties/test/gem_dependency_test.rb
@@ -9,33 +9,33 @@ Rails::VendorGemSourceIndex.silence_spec_warnings = true
uses_mocha "Plugin Tests" do
class GemDependencyTest < Test::Unit::TestCase
def setup
- @gem = Rails::GemDependency.new "hpricot"
- @gem_with_source = Rails::GemDependency.new "hpricot", :source => "http://code.whytheluckystiff.net"
- @gem_with_version = Rails::GemDependency.new "hpricot", :version => "= 0.6"
- @gem_with_lib = Rails::GemDependency.new "aws-s3", :lib => "aws/s3"
- @gem_without_load = Rails::GemDependency.new "hpricot", :lib => false
+ @gem = Rails::GemDependency.new "xhpricotx"
+ @gem_with_source = Rails::GemDependency.new "xhpricotx", :source => "http://code.whytheluckystiff.net"
+ @gem_with_version = Rails::GemDependency.new "xhpricotx", :version => "= 0.6"
+ @gem_with_lib = Rails::GemDependency.new "xaws-s3x", :lib => "aws/s3"
+ @gem_without_load = Rails::GemDependency.new "xhpricotx", :lib => false
end
def test_configuration_adds_gem_dependency
config = Rails::Configuration.new
- config.gem "aws-s3", :lib => "aws/s3", :version => "0.4.0"
- assert_equal [["install", "aws-s3", "--version", '"= 0.4.0"']], config.gems.collect(&:install_command)
+ config.gem "xaws-s3x", :lib => "aws/s3", :version => "0.4.0"
+ assert_equal [["install", "xaws-s3x", "--version", '"= 0.4.0"']], config.gems.collect(&:install_command)
end
def test_gem_creates_install_command
- assert_equal %w(install hpricot), @gem.install_command
+ assert_equal %w(install xhpricotx), @gem.install_command
end
def test_gem_with_source_creates_install_command
- assert_equal %w(install hpricot --source http://code.whytheluckystiff.net), @gem_with_source.install_command
+ assert_equal %w(install xhpricotx --source http://code.whytheluckystiff.net), @gem_with_source.install_command
end
def test_gem_with_version_creates_install_command
- assert_equal ["install", "hpricot", "--version", '"= 0.6"'], @gem_with_version.install_command
+ assert_equal ["install", "xhpricotx", "--version", '"= 0.6"'], @gem_with_version.install_command
end
def test_gem_creates_unpack_command
- assert_equal %w(unpack hpricot), @gem.unpack_command
+ assert_equal %w(unpack xhpricotx), @gem.unpack_command
end
def test_gem_with_version_unpack_install_command
@@ -43,7 +43,7 @@ uses_mocha "Plugin Tests" do
mock_spec = mock()
mock_spec.stubs(:version).returns('0.6')
@gem_with_version.stubs(:specification).returns(mock_spec)
- assert_equal ["unpack", "hpricot", "--version", '= 0.6'], @gem_with_version.unpack_command
+ assert_equal ["unpack", "xhpricotx", "--version", '= 0.6'], @gem_with_version.unpack_command
end
def test_gem_adds_load_paths
@@ -134,7 +134,6 @@ uses_mocha "Plugin Tests" do
dummy_gem.add_load_paths
dummy_gem.load
assert dummy_gem.loaded?
- debugger
assert_equal 2, dummy_gem.dependencies.size
assert_nothing_raised do
dummy_gem.dependencies.each do |g|
diff --git a/railties/test/generators/rails_template_runner_test.rb b/railties/test/generators/rails_template_runner_test.rb
new file mode 100644
index 0000000000..fcc020603d
--- /dev/null
+++ b/railties/test/generators/rails_template_runner_test.rb
@@ -0,0 +1,190 @@
+require 'abstract_unit'
+require 'generators/generator_test_helper'
+
+class RailsTemplateRunnerTest < GeneratorTestCase
+ def setup
+ Rails::Generator::Base.use_application_sources!
+ run_generator('app', [RAILS_ROOT])
+ # generate empty template
+ @template_path = File.join(RAILS_ROOT, 'template.rb')
+ File.open(File.join(@template_path), 'w') {|f| f << '' }
+
+ @git_plugin_uri = 'git://github.com/technoweenie/restful-authentication.git'
+ @svn_plugin_uri = 'svn://svnhub.com/technoweenie/restful-authentication/trunk'
+ end
+
+ def teardown
+ super
+ rm_rf "#{RAILS_ROOT}/README"
+ rm_rf "#{RAILS_ROOT}/Rakefile"
+ rm_rf "#{RAILS_ROOT}/doc"
+ rm_rf "#{RAILS_ROOT}/lib"
+ rm_rf "#{RAILS_ROOT}/log"
+ rm_rf "#{RAILS_ROOT}/script"
+ rm_rf "#{RAILS_ROOT}/vendor"
+ rm_rf "#{RAILS_ROOT}/tmp"
+ rm_rf "#{RAILS_ROOT}/Capfile"
+ rm_rf @template_path
+ end
+
+ def test_initialize_should_load_template
+ Rails::TemplateRunner.any_instance.expects(:load_template).with(@template_path)
+ silence_generator do
+ Rails::TemplateRunner.new(@template_path, RAILS_ROOT)
+ end
+ end
+
+ def test_initialize_should_raise_error_on_missing_template_file
+ assert_raise(RuntimeError) do
+ silence_generator do
+ Rails::TemplateRunner.new('non/existent/path/to/template.rb', RAILS_ROOT)
+ end
+ end
+ end
+
+ def test_file_should_write_data_to_file_path
+ run_template_method(:file, 'lib/test_file.rb', 'heres test data')
+ assert_generated_file_with_data 'lib/test_file.rb', 'heres test data'
+ end
+
+ def test_file_should_write_block_contents_to_file_path
+ run_template_method(:file, 'lib/test_file.rb') { 'heres block data' }
+ assert_generated_file_with_data 'lib/test_file.rb', 'heres block data'
+ end
+
+ def test_plugin_with_git_option_should_run_plugin_install
+ expects_run_with_command("script/plugin install #{@git_plugin_uri}")
+ run_template_method(:plugin, 'restful-authentication', :git => @git_plugin_uri)
+ end
+
+ def test_plugin_with_svn_option_should_run_plugin_install
+ expects_run_with_command("script/plugin install #{@svn_plugin_uri}")
+ run_template_method(:plugin, 'restful-authentication', :svn => @svn_plugin_uri)
+ end
+
+ def test_plugin_with_git_option_and_submodule_should_use_git_scm
+ Rails::Git.expects(:run).with("submodule add #{@git_plugin_uri} vendor/plugins/rest_auth")
+ run_template_method(:plugin, 'rest_auth', :git => @git_plugin_uri, :submodule => true)
+ end
+
+ def test_plugin_with_no_options_should_skip_method
+ Rails::TemplateRunner.any_instance.expects(:run).never
+ run_template_method(:plugin, 'rest_auth', {})
+ end
+
+ def test_gem_should_put_gem_dependency_in_enviroment
+ run_template_method(:gem, 'will-paginate')
+ assert_rails_initializer_includes("config.gem 'will-paginate'")
+ end
+
+ def test_gem_with_options_should_include_options_in_gem_dependency_in_environment
+ run_template_method(:gem, 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com')
+ assert_rails_initializer_includes("config.gem 'mislav-will-paginate', :lib => 'will-paginate', :source => 'http://gems.github.com'")
+ end
+
+ def test_environment_should_include_data_in_environment_initializer_block
+ load_paths = 'config.load_paths += %w["#{RAILS_ROOT}/app/extras"]'
+ run_template_method(:environment, load_paths)
+ assert_rails_initializer_includes(load_paths)
+ end
+
+ def test_environment_with_block_should_include_block_contents_in_environment_initializer_block
+ run_template_method(:environment) do
+ '# This wont be added'
+ '# This will be added'
+ end
+ assert_rails_initializer_includes('# This will be added')
+ end
+
+ def test_git_with_symbol_should_run_command_using_git_scm
+ Rails::Git.expects(:run).once.with('init')
+ run_template_method(:git, :init)
+ end
+
+ def test_git_with_hash_should_run_each_command_using_git_scm
+ Rails::Git.expects(:run).times(2)
+ run_template_method(:git, {:init => '', :add => '.'})
+ end
+
+ def test_vendor_should_write_data_to_file_in_vendor
+ run_template_method(:vendor, 'vendor_file.rb', '# vendor data')
+ assert_generated_file_with_data('vendor/vendor_file.rb', '# vendor data')
+ end
+
+ def test_lib_should_write_data_to_file_in_lib
+ run_template_method(:lib, 'my_library.rb', 'class MyLibrary')
+ assert_generated_file_with_data('lib/my_library.rb', 'class MyLibrary')
+ end
+
+ def test_rakefile_should_write_date_to_file_in_lib_tasks
+ run_template_method(:rakefile, 'myapp.rake', 'task :run => [:environment]')
+ assert_generated_file_with_data('lib/tasks/myapp.rake', 'task :run => [:environment]')
+ end
+
+ def test_initializer_should_write_date_to_file_in_config_initializers
+ run_template_method(:initializer, 'constants.rb', 'MY_CONSTANT = 42')
+ assert_generated_file_with_data('config/initializers/constants.rb', 'MY_CONSTANT = 42')
+ end
+
+ def test_generate_should_run_script_generate_with_argument_and_options
+ expects_run_with_command('script/generate model MyModel')
+ run_template_method(:generate, 'model', 'MyModel')
+ end
+
+ def test_rake_should_run_rake_command_with_development_env
+ expects_run_with_command('rake log:clear RAILS_ENV=development')
+ run_template_method(:rake, 'log:clear')
+ end
+
+ def test_rake_with_env_option_should_run_rake_command_in_env
+ expects_run_with_command('rake log:clear RAILS_ENV=production')
+ run_template_method(:rake, 'log:clear', :env => 'production')
+ end
+
+ def test_rake_with_sudo_option_should_run_rake_command_with_sudo
+ expects_run_with_command('sudo rake log:clear RAILS_ENV=development')
+ run_template_method(:rake, 'log:clear', :sudo => true)
+ end
+
+ def test_capify_should_run_the_capify_command
+ expects_run_with_command('capify .')
+ run_template_method(:capify!)
+ end
+
+ def test_freeze_should_freeze_rails_edge
+ expects_run_with_command('rake rails:freeze:edge')
+ run_template_method(:freeze!)
+ end
+
+ def test_route_should_add_data_to_the_routes_block_in_config_routes
+ route_command = "map.route '/login', :controller => 'sessions', :action => 'new'"
+ run_template_method(:route, route_command)
+ assert_generated_file_with_data 'config/routes.rb', route_command
+ end
+
+ protected
+ def run_template_method(method_name, *args, &block)
+ silence_generator do
+ @template_runner = Rails::TemplateRunner.new(@template_path, RAILS_ROOT)
+ @template_runner.send(method_name, *args, &block)
+ end
+ end
+
+ def expects_run_with_command(command)
+ Rails::TemplateRunner.any_instance.stubs(:run).once.with(command, false)
+ end
+
+ def assert_rails_initializer_includes(data, message = nil)
+ message ||= "Rails::Initializer should include #{data}"
+ assert_generated_file 'config/environment.rb' do |body|
+ assert_match(/#{Regexp.escape("Rails::Initializer.run do |config|")}.+#{Regexp.escape(data)}.+end/m, body, message)
+ end
+ end
+
+ def assert_generated_file_with_data(file, data, message = nil)
+ message ||= "#{file} should include '#{data}'"
+ assert_generated_file(file) do |file|
+ assert_match(/#{Regexp.escape(data)}/,file, message)
+ end
+ end
+end \ No newline at end of file