aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Gemfile5
-rw-r--r--README.rdoc2
-rw-r--r--RELEASING_RAILS.rdoc3
-rw-r--r--actionmailer/CHANGELOG.md19
-rw-r--r--actionmailer/lib/action_mailer/base.rb11
-rw-r--r--actionmailer/test/base_test.rb6
-rw-r--r--actionmailer/test/mailers/base_mailer.rb4
-rw-r--r--actionpack/CHANGELOG.md117
-rw-r--r--actionpack/RUNNING_UNIT_TESTS.rdoc (renamed from actionpack/RUNNING_UNIT_TESTS)0
-rw-r--r--actionpack/actionpack.gemspec4
-rw-r--r--actionpack/lib/abstract_controller/translation.rb9
-rw-r--r--actionpack/lib/action_controller/caching.rb4
-rw-r--r--actionpack/lib/action_controller/metal.rb1
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb2
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb1
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb13
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb178
-rw-r--r--actionpack/lib/action_controller/railtie.rb19
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb22
-rw-r--r--actionpack/lib/action_controller/test_case.rb33
-rw-r--r--actionpack/lib/action_dispatch.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb18
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/best_standards_support.rb30
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb17
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb108
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb4
-rw-r--r--actionpack/lib/action_dispatch/request/session.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb40
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb12
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb181
-rw-r--r--actionpack/lib/action_dispatch/routing/routes_proxy.rb2
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb22
-rw-r--r--actionpack/lib/action_view/helpers/asset_url_helper.rb3
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb26
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb49
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/record_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tags/date_select.rb2
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb2
-rw-r--r--actionpack/lib/action_view/template.rb3
-rw-r--r--actionpack/lib/action_view/template/error.rb2
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb2
-rw-r--r--actionpack/lib/action_view/test_case.rb6
-rw-r--r--actionpack/test/abstract/translation_test.rb81
-rw-r--r--actionpack/test/abstract_unit.rb2
-rw-r--r--actionpack/test/activerecord/form_helper_activerecord_test.rb91
-rw-r--r--actionpack/test/controller/integration_test.rb6
-rw-r--r--actionpack/test/controller/layout_test.rb3
-rw-r--r--actionpack/test/controller/localized_templates_test.rb8
-rw-r--r--actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb50
-rw-r--r--actionpack/test/controller/parameters/nested_parameters_test.rb62
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb126
-rw-r--r--actionpack/test/controller/parameters/raise_on_unpermitted_params_test.rb33
-rw-r--r--actionpack/test/controller/record_identifier_test.rb34
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb39
-rw-r--r--actionpack/test/controller/resources_test.rb1
-rw-r--r--actionpack/test/controller/webservice_test.rb13
-rw-r--r--actionpack/test/dispatch/best_standards_support_test.rb35
-rw-r--r--actionpack/test/dispatch/cookies_test.rb50
-rw-r--r--actionpack/test/dispatch/request_test.rb4
-rw-r--r--actionpack/test/dispatch/response_test.rb8
-rw-r--r--actionpack/test/dispatch/routing/inspector_test.rb8
-rw-r--r--actionpack/test/dispatch/routing_test.rb70
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb4
-rw-r--r--actionpack/test/dispatch/session/test_session_test.rb16
-rw-r--r--actionpack/test/dispatch/test_request_test.rb6
-rw-r--r--actionpack/test/fixtures/developer.rb1
-rw-r--r--actionpack/test/fixtures/test/_directory/_partial_with_locales.html.erb1
-rw-r--r--actionpack/test/fixtures/test/render_partial_inside_directory.html.erb1
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb3
-rw-r--r--actionpack/test/template/date_helper_i18n_test.rb11
-rw-r--r--actionpack/test/template/date_helper_test.rb65
-rw-r--r--actionpack/test/template/form_helper_test.rb703
-rw-r--r--actionpack/test/template/javascript_helper_test.rb4
-rw-r--r--actionpack/test/template/record_tag_helper_test.rb6
-rw-r--r--actionpack/test/template/test_case_test.rb21
-rw-r--r--actionpack/test/template/text_helper_test.rb6
-rw-r--r--actionpack/test/template/url_helper_test.rb6
-rw-r--r--activemodel/lib/active_model/callbacks.rb2
-rw-r--r--activemodel/test/cases/naming_test.rb8
-rw-r--r--activemodel/test/cases/validations/length_validation_test.rb117
-rw-r--r--activerecord/CHANGELOG.md169
-rw-r--r--activerecord/RUNNING_UNIT_TESTS.rdoc (renamed from activerecord/RUNNING_UNIT_TESTS)0
-rw-r--r--activerecord/Rakefile5
-rw-r--r--activerecord/activerecord.gemspec2
-rw-r--r--activerecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb13
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb3
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb1
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb3
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb25
-rw-r--r--activerecord/lib/active_record/base.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb22
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb27
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb36
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb80
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb23
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb93
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb5
-rw-r--r--activerecord/lib/active_record/core.rb21
-rw-r--r--activerecord/lib/active_record/fixtures.rb4
-rw-r--r--activerecord/lib/active_record/migration.rb2
-rw-r--r--activerecord/lib/active_record/persistence.rb30
-rw-r--r--activerecord/lib/active_record/querying.rb1
-rw-r--r--activerecord/lib/active_record/railties/databases.rake12
-rw-r--r--activerecord/lib/active_record/reflection.rb1
-rw-r--r--activerecord/lib/active_record/relation.rb12
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb3
-rw-r--r--activerecord/lib/active_record/relation/merger.rb36
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb17
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb5
-rw-r--r--activerecord/lib/active_record/sanitization.rb8
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb15
-rw-r--r--activerecord/lib/active_record/scoping.rb2
-rw-r--r--activerecord/lib/active_record/scoping/default.rb4
-rw-r--r--activerecord/lib/active_record/store.rb33
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb4
-rw-r--r--activerecord/lib/active_record/test_case.rb7
-rw-r--r--activerecord/lib/active_record/transactions.rb5
-rw-r--r--activerecord/test/cases/adapter_test.rb10
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb8
-rw-r--r--activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb9
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb308
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb48
-rw-r--r--activerecord/test/cases/adapters/postgresql/intrange_test.rb106
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb9
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb9
-rw-r--r--activerecord/test/cases/ar_schema_test.rb1
-rw-r--r--activerecord/test/cases/associations/eager_test.rb6
-rw-r--r--activerecord/test/cases/associations/habtm_join_table_test.rb35
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb44
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb7
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb9
-rw-r--r--activerecord/test/cases/binary_test.rb2
-rw-r--r--activerecord/test/cases/calculations_test.rb4
-rw-r--r--activerecord/test/cases/connection_pool_test.rb11
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb48
-rw-r--r--activerecord/test/cases/dirty_test.rb15
-rw-r--r--activerecord/test/cases/dup_test.rb26
-rw-r--r--activerecord/test/cases/helper.rb2
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb6
-rw-r--r--activerecord/test/cases/migration_test.rb18
-rw-r--r--activerecord/test/cases/persistence_test.rb6
-rw-r--r--activerecord/test/cases/quoting_test.rb14
-rw-r--r--activerecord/test/cases/relation/where_test.rb40
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb28
-rw-r--r--activerecord/test/cases/relations_test.rb20
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb16
-rw-r--r--activerecord/test/cases/store_test.rb12
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb6
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb27
-rw-r--r--activerecord/test/cases/transactions_test.rb28
-rw-r--r--activerecord/test/fixtures/readers.yml4
-rw-r--r--activerecord/test/models/admin/user.rb27
-rw-r--r--activerecord/test/models/author.rb13
-rw-r--r--activerecord/test/models/category.rb5
-rw-r--r--activerecord/test/models/comment.rb6
-rw-r--r--activerecord/test/models/developer.rb9
-rw-r--r--activerecord/test/models/person.rb1
-rw-r--r--activerecord/test/models/post.rb40
-rw-r--r--activerecord/test/models/project.rb3
-rw-r--r--activerecord/test/models/reader.rb1
-rw-r--r--activerecord/test/models/topic.rb1
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb26
-rw-r--r--activerecord/test/schema/schema.rb3
-rw-r--r--activesupport/CHANGELOG.md24
-rw-r--r--activesupport/lib/active_support/cache.rb66
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb52
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb53
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb7
-rw-r--r--activesupport/lib/active_support/gzip.rb4
-rw-r--r--activesupport/lib/active_support/i18n.rb3
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb32
-rw-r--r--activesupport/lib/active_support/locale/en.yml6
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb1
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb2
-rw-r--r--activesupport/lib/active_support/proxy_object.rb2
-rw-r--r--activesupport/lib/active_support/railtie.rb30
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb61
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb6
-rw-r--r--activesupport/lib/active_support/values/unicode_tables.datbin904408 -> 904483 bytes
-rw-r--r--activesupport/test/abstract_unit.rb2
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb17
-rw-r--r--activesupport/test/core_ext/kernel_test.rb12
-rw-r--r--activesupport/test/core_ext/object_and_class_ext_test.rb4
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb86
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb6
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb8
-rw-r--r--activesupport/test/deprecation_test.rb6
-rw-r--r--activesupport/test/gzip_test.rb18
-rw-r--r--activesupport/test/i18n_test.rb2
-rw-r--r--activesupport/test/inflector_test_cases.rb1
-rw-r--r--activesupport/test/ordered_hash_test.rb1
-rw-r--r--activesupport/test/rescuable_test.rb3
-rw-r--r--guides/assets/images/getting_started/forbidden_attributes_for_new_post.pngbin0 -> 33796 bytes
-rw-r--r--guides/assets/images/getting_started/routing_error_no_controller.pngbin6268 -> 9791 bytes
-rw-r--r--guides/assets/images/getting_started/routing_error_no_route_matches.pngbin6508 -> 11238 bytes
-rw-r--r--guides/assets/images/getting_started/template_is_missing_posts_new.pngbin5851 -> 21327 bytes
-rw-r--r--guides/assets/images/getting_started/unknown_action_create_for_posts.pngbin4146 -> 11780 bytes
-rw-r--r--guides/assets/images/getting_started/unknown_action_new_for_posts.pngbin4208 -> 12795 bytes
-rw-r--r--guides/code/getting_started/.gitignore16
-rw-r--r--guides/code/getting_started/Gemfile24
-rw-r--r--guides/code/getting_started/Gemfile.lock150
-rw-r--r--guides/code/getting_started/README.rdoc6
-rw-r--r--guides/code/getting_started/Rakefile1
-rw-r--r--guides/code/getting_started/app/assets/javascripts/application.js1
-rw-r--r--guides/code/getting_started/app/assets/javascripts/comments.js.coffee3
-rw-r--r--guides/code/getting_started/app/assets/javascripts/posts.js.coffee3
-rw-r--r--guides/code/getting_started/app/assets/javascripts/welcome.js.coffee3
-rw-r--r--guides/code/getting_started/app/assets/stylesheets/application.css2
-rw-r--r--guides/code/getting_started/app/assets/stylesheets/comments.css.scss3
-rw-r--r--guides/code/getting_started/app/assets/stylesheets/posts.css.scss3
-rw-r--r--guides/code/getting_started/app/assets/stylesheets/welcome.css.scss3
-rw-r--r--guides/code/getting_started/app/controllers/application_controller.rb4
-rw-r--r--guides/code/getting_started/app/controllers/comments_controller.rb6
-rw-r--r--guides/code/getting_started/app/controllers/concerns/.keep (renamed from guides/code/getting_started/app/mailers/.gitkeep)0
-rw-r--r--guides/code/getting_started/app/controllers/posts_controller.rb34
-rw-r--r--guides/code/getting_started/app/mailers/.keep (renamed from guides/code/getting_started/app/models/.gitkeep)0
-rw-r--r--guides/code/getting_started/app/models/.keep (renamed from guides/code/getting_started/lib/assets/.gitkeep)0
-rw-r--r--guides/code/getting_started/app/models/concerns/.keep (renamed from guides/code/getting_started/lib/tasks/.gitkeep)0
-rw-r--r--guides/code/getting_started/app/models/post.rb9
-rw-r--r--guides/code/getting_started/app/views/comments/_comment.html.erb6
-rw-r--r--guides/code/getting_started/app/views/layouts/application.html.erb4
-rw-r--r--guides/code/getting_started/app/views/posts/_form.html.erb26
-rw-r--r--guides/code/getting_started/app/views/posts/edit.html.erb8
-rw-r--r--guides/code/getting_started/app/views/posts/index.html.erb14
-rw-r--r--guides/code/getting_started/app/views/posts/new.html.erb6
-rw-r--r--guides/code/getting_started/app/views/posts/show.html.erb6
-rw-r--r--guides/code/getting_started/app/views/welcome/index.html.erb3
-rwxr-xr-xguides/code/getting_started/bin/bundle4
-rwxr-xr-xguides/code/getting_started/bin/rails4
-rwxr-xr-xguides/code/getting_started/bin/rake4
-rw-r--r--guides/code/getting_started/config/application.rb39
-rw-r--r--guides/code/getting_started/config/environment.rb4
-rw-r--r--guides/code/getting_started/config/environments/development.rb14
-rw-r--r--guides/code/getting_started/config/environments/production.rb39
-rw-r--r--guides/code/getting_started/config/environments/test.rb12
-rw-r--r--guides/code/getting_started/config/initializers/filter_parameter_logging.rb4
-rw-r--r--guides/code/getting_started/config/initializers/inflections.rb11
-rw-r--r--guides/code/getting_started/config/initializers/locale.rb (renamed from railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb)0
-rw-r--r--guides/code/getting_started/config/initializers/secret_token.rb7
-rw-r--r--guides/code/getting_started/config/initializers/session_store.rb2
-rw-r--r--guides/code/getting_started/config/initializers/wrap_parameters.rb12
-rw-r--r--guides/code/getting_started/config/locales/en.yml22
-rw-r--r--guides/code/getting_started/config/routes.rb60
-rw-r--r--guides/code/getting_started/db/migrate/20130122042648_create_posts.rb (renamed from guides/code/getting_started/db/migrate/20120420083127_create_posts.rb)0
-rw-r--r--guides/code/getting_started/db/migrate/20130122045842_create_comments.rb (renamed from guides/code/getting_started/db/migrate/20110901012815_create_comments.rb)3
-rw-r--r--guides/code/getting_started/db/schema.rb27
-rw-r--r--guides/code/getting_started/lib/assets/.keep (renamed from guides/code/getting_started/test/fixtures/.gitkeep)0
-rw-r--r--guides/code/getting_started/lib/tasks/.keep (renamed from guides/code/getting_started/test/functional/.gitkeep)0
-rw-r--r--guides/code/getting_started/log/.keep (renamed from guides/code/getting_started/test/integration/.gitkeep)0
-rw-r--r--guides/code/getting_started/public/404.html3
-rw-r--r--guides/code/getting_started/public/422.html2
-rw-r--r--guides/code/getting_started/public/500.html3
-rwxr-xr-xguides/code/getting_started/script/rails6
-rw-r--r--guides/code/getting_started/test/controllers/.keep (renamed from guides/code/getting_started/test/unit/.gitkeep)0
-rw-r--r--guides/code/getting_started/test/controllers/comments_controller_test.rb (renamed from guides/code/getting_started/test/functional/comments_controller_test.rb)0
-rw-r--r--guides/code/getting_started/test/controllers/posts_controller_test.rb (renamed from guides/code/getting_started/test/unit/tag_test.rb)2
-rw-r--r--guides/code/getting_started/test/controllers/welcome_controller_test.rb (renamed from guides/code/getting_started/test/functional/welcome_controller_test.rb)1
-rw-r--r--guides/code/getting_started/test/fixtures/.keep (renamed from guides/code/getting_started/vendor/plugins/.gitkeep)0
-rw-r--r--guides/code/getting_started/test/fixtures/comments.yml6
-rw-r--r--guides/code/getting_started/test/fixtures/posts.yml2
-rw-r--r--guides/code/getting_started/test/functional/posts_controller_test.rb49
-rw-r--r--guides/code/getting_started/test/helpers/.keep0
-rw-r--r--guides/code/getting_started/test/helpers/comments_helper_test.rb (renamed from guides/code/getting_started/test/unit/helpers/comments_helper_test.rb)0
-rw-r--r--guides/code/getting_started/test/helpers/posts_helper_test.rb (renamed from guides/code/getting_started/test/unit/helpers/posts_helper_test.rb)0
-rw-r--r--guides/code/getting_started/test/helpers/welcome_helper_test.rb4
-rw-r--r--guides/code/getting_started/test/integration/.keep0
-rw-r--r--guides/code/getting_started/test/mailers/.keep0
-rw-r--r--guides/code/getting_started/test/models/.keep0
-rw-r--r--guides/code/getting_started/test/models/comment_test.rb (renamed from guides/code/getting_started/test/unit/comment_test.rb)0
-rw-r--r--guides/code/getting_started/test/models/post_test.rb (renamed from guides/code/getting_started/test/unit/post_test.rb)0
-rw-r--r--guides/code/getting_started/test/test_helper.rb2
-rw-r--r--guides/code/getting_started/test/unit/helpers/home_helper_test.rb4
-rw-r--r--guides/code/getting_started/vendor/assets/javascripts/.keep0
-rw-r--r--guides/code/getting_started/vendor/assets/stylesheets/.keep0
-rw-r--r--guides/source/4_0_release_notes.md7
-rw-r--r--guides/source/action_controller_overview.md4
-rw-r--r--guides/source/action_mailer_basics.md22
-rw-r--r--guides/source/active_record_basics.md2
-rw-r--r--guides/source/active_record_callbacks.md1
-rw-r--r--guides/source/active_support_core_extensions.md2
-rw-r--r--guides/source/asset_pipeline.md20
-rw-r--r--guides/source/caching_with_rails.md47
-rw-r--r--guides/source/command_line.md6
-rw-r--r--guides/source/configuring.md48
-rw-r--r--guides/source/development_dependencies_install.md14
-rw-r--r--guides/source/engines.md41
-rw-r--r--guides/source/generators.md2
-rw-r--r--guides/source/getting_started.md54
-rw-r--r--guides/source/i18n.md2
-rw-r--r--guides/source/layouts_and_rendering.md93
-rw-r--r--guides/source/migrations.md4
-rw-r--r--guides/source/nested_model_forms.md2
-rw-r--r--guides/source/plugins.md2
-rw-r--r--guides/source/rails_on_rack.md6
-rw-r--r--guides/source/routing.md13
-rw-r--r--guides/source/security.md1
-rw-r--r--guides/source/testing.md2
-rw-r--r--guides/source/upgrading_ruby_on_rails.md10
-rw-r--r--guides/source/working_with_javascript_in_rails.md2
-rw-r--r--railties/CHANGELOG.md44
-rw-r--r--railties/lib/rails/application.rb10
-rw-r--r--railties/lib/rails/commands/application.rb19
-rw-r--r--railties/lib/rails/configuration.rb4
-rw-r--r--railties/lib/rails/engine.rb16
-rw-r--r--railties/lib/rails/engine/configuration.rb24
-rw-r--r--railties/lib/rails/engine/railties.rb29
-rw-r--r--railties/lib/rails/generators/app_base.rb10
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/bundle1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb2
-rw-r--r--railties/lib/rails/info.rb2
-rw-r--r--railties/lib/rails/paths.rb44
-rw-r--r--railties/lib/rails/ruby_version_check.rb4
-rw-r--r--railties/lib/rails/tasks/log.rake22
-rw-r--r--railties/lib/rails/test_unit/testing.rake12
-rw-r--r--railties/railties.gemspec2
-rw-r--r--railties/test/application/configuration_test.rb60
-rw-r--r--railties/test/application/initializers/load_path_test.rb2
-rw-r--r--railties/test/application/middleware/best_practices_test.rb30
-rw-r--r--railties/test/application/middleware_test.rb3
-rw-r--r--railties/test/application/paths_test.rb10
-rw-r--r--railties/test/application/runner_test.rb12
-rw-r--r--railties/test/generators/app_generator_test.rb7
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb1
-rw-r--r--railties/test/isolation/abstract_unit.rb2
-rw-r--r--railties/test/paths_test.rb56
-rw-r--r--railties/test/railties/engine_test.rb6
-rw-r--r--railties/test/railties/mounted_engine_test.rb9
358 files changed, 4700 insertions, 2054 deletions
diff --git a/.gitignore b/.gitignore
index 854fdbf450..a3a5304ecd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,7 @@
debug.log
.Gemfile
/.bundle
-/.rbenv-version
-/.rvmrc
+/.ruby-version
/Gemfile.lock
/pkg
/dist
diff --git a/Gemfile b/Gemfile
index 73ac0172ae..09d0f502ce 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,13 +8,10 @@ gem 'mocha', '~> 0.13.0', require: false
gem 'rack-test', github: 'brynary/rack-test'
gem 'rack-cache', '~> 1.2'
gem 'bcrypt-ruby', '~> 3.0.0'
-gem 'jquery-rails', '~> 2.1.4', github: 'rails/jquery-rails'
+gem 'jquery-rails', '~> 2.2.0', github: 'rails/jquery-rails'
gem 'turbolinks'
gem 'coffee-rails', github: 'rails/coffee-rails'
-# TODO: Release thor
-gem 'thor', github: 'wycats/thor', branch: 'master'
-
gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders', branch: 'master'
# Needed for compiling the ActionDispatch::Journey parser
diff --git a/README.rdoc b/README.rdoc
index 91a5f27add..bb9c418e0b 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -27,7 +27,7 @@ and render view templates in order to generate the appropriate HTTP response.
In Rails, the Controller and View layers are handled together by Action Pack.
These two layers are bundled in a single package due to their heavy interdependence.
-This is unlike the relationship between Active Record and Action Pack which are
+This is unlike the relationship between Active Record and Action Pack, which are
independent. Each of these packages can be used independently outside of Rails. You
can read more about Action Pack in its {README}[link:/rails/rails/blob/master/actionpack/README.rdoc].
diff --git a/RELEASING_RAILS.rdoc b/RELEASING_RAILS.rdoc
index 9af79f73e2..b065be4922 100644
--- a/RELEASING_RAILS.rdoc
+++ b/RELEASING_RAILS.rdoc
@@ -42,6 +42,9 @@ addressed, and that can impact your release date.
Ruby implementors have high stakes in making sure Rails works. Be kind and
give them a heads up that Rails will be released soonish.
+This only needs done for major and minor releases, bugfix releases aren't a
+big enough deal, and are supposed to be backwards compatible.
+
Send an email just giving a heads up about the upcoming release to these
lists:
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 43bfa536c1..7e57fb39f3 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,6 +1,23 @@
## Rails 4.0.0 (unreleased) ##
-* Eager loading made to use relation's in_clause_length instead of host's one.
+* Allow passing interpolations to `#default_i18n_subject`, e.g.:
+
+ # config/locales/en.yml
+ en:
+ user_mailer:
+ welcome:
+ subject: 'Hello, %{username}'
+
+ # app/mailers/user_mailer.rb
+ class UserMailer < ActionMailer::Base
+ def welcome(user)
+ mail(subject: default_i18n_subject(username: user.name))
+ end
+ end
+
+ *Olek Janiszewski*
+
+* Eager loading made to use relation's `in_clause_length` instead of host's one.
Fix #8474
*Boris Staal*
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 9ba606c045..ff74185e37 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -726,9 +726,10 @@ module ActionMailer
# Translates the +subject+ using Rails I18n class under <tt>[mailer_scope, action_name]</tt> scope.
# If it does not find a translation for the +subject+ under the specified scope it will default to a
# humanized version of the <tt>action_name</tt>.
- def default_i18n_subject #:nodoc:
+ # If the subject has interpolations, you can pass them through the +interpolations+ parameter.
+ def default_i18n_subject(interpolations = {})
mailer_scope = self.class.mailer_name.tr('/', '.')
- I18n.t(:subject, scope: [mailer_scope, action_name], default: action_name.humanize)
+ I18n.t(:subject, interpolations.merge(scope: [mailer_scope, action_name], default: action_name.humanize))
end
def collect_responses(headers) #:nodoc:
@@ -747,7 +748,7 @@ module ActionMailer
templates_path = headers.delete(:template_path) || self.class.mailer_name
templates_name = headers.delete(:template_name) || action_name
- each_template(templates_path, templates_name) do |template|
+ each_template(Array(templates_path), templates_name) do |template|
self.formats = template.formats
responses << {
@@ -761,9 +762,9 @@ module ActionMailer
end
def each_template(paths, name, &block) #:nodoc:
- templates = lookup_context.find_all(name, Array(paths))
+ templates = lookup_context.find_all(name, paths)
if templates.empty?
- raise ActionView::MissingTemplate.new([paths], name, [paths], false, 'mailer')
+ raise ActionView::MissingTemplate.new(paths, name, paths, false, 'mailer')
else
templates.uniq { |t| t.formats }.each(&block)
end
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index b06c465380..b9c56c540d 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -209,6 +209,12 @@ class BaseTest < ActiveSupport::TestCase
assert_equal "New Subject!", email.subject
end
+ test 'default subject can have interpolations' do
+ I18n.backend.store_translations('en', base_mailer: {with_subject_interpolations: {subject: 'Will the real %{rapper_or_impersonator} please stand up?'}})
+ email = BaseMailer.with_subject_interpolations
+ assert_equal 'Will the real Slim Shady please stand up?', email.subject
+ end
+
test "translations are scoped properly" do
I18n.backend.store_translations('en', base_mailer: {email_with_translations: {greet_user: "Hello %{name}!"}})
email = BaseMailer.email_with_translations
diff --git a/actionmailer/test/mailers/base_mailer.rb b/actionmailer/test/mailers/base_mailer.rb
index 8fca6177bd..504ca36483 100644
--- a/actionmailer/test/mailers/base_mailer.rb
+++ b/actionmailer/test/mailers/base_mailer.rb
@@ -123,4 +123,8 @@ class BaseMailer < ActionMailer::Base
mail(:template_name => "welcome")
nil
end
+
+ def with_subject_interpolations
+ mail(subject: default_i18n_subject(rapper_or_impersonator: 'Slim Shady'), body: '')
+ end
end
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index a3d2274b61..6940683c8c 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,9 +1,104 @@
## Rails 4.0.0 (unreleased) ##
-* `BestStandardsSupport` no longer duplicates `X-UA-Compatible` values on
- each request to prevent header size from blowing up.
+* Change `image_alt` method to replace underscores/hyphens to spaces in filenames.
- *Edward Anderson*
+ Previously, underscored filenames became `alt="A_long_file_name_with_underscores"`
+ in HTML, which is poor for accessibility. For instance, Apple's VoiceOver Utility
+ pronounces each underscore. `A_long_file_name` thus would be read as `A underscore
+ long underscore file underscore name.` Now underscored or hyphenated filenames
+ (both of which are very popular naming conventions) read more naturally in
+ screen readers by converting both hyphens and underscores to spaces.
+
+ Before:
+ image_tag('underscored_file_name.png')
+ # => <img alt="Underscored_file_name" src="/assets/underscored_file_name.png" />
+
+ After:
+ image_tag('underscored_file_name.png')
+ # => <img alt="Underscored file name" src="/assets/underscored_file_name.png" />
+
+ *Nick Cox*
+
+* We don't support the `:controller` option for route definitions
+ with the ruby constant notation. This will now result in an
+ `ArgumentError`.
+
+ Example:
+ # This raises an ArgumentError:
+ resources :posts, :controller => "Admin::Posts"
+
+ # Use directory notation instead:
+ resources :posts, :controller => "admin/posts"
+
+ *Yves Senn*
+
+* `assert_template` can be used to verify the locals of partials,
+ which live inside a directory.
+ Fixes #8516.
+
+ # Prefixed partials inside directories worked and still work.
+ assert_template partial: 'directory/_partial', locals: {name: 'John'}
+
+ # This did not work but does now.
+ assert_template partial: 'directory/partial', locals: {name: 'John'}
+
+ *Yves Senn*
+
+* Fix `content_tag_for` with array html option.
+ It would embed array as string instead of joining it like `content_tag` does:
+
+ content_tag(:td, class: ["foo", "bar"]){}
+ #=> '<td class="foo bar"></td>'
+
+ Before:
+
+ content_tag_for(:td, item, class: ["foo", "bar"])
+ #=> '<td class="item [&quot;foo&quot;, &quot;bar&quot;]" id="item_1"></td>'
+
+ After:
+
+ content_tag_for(:td, item, class: ["foo", "bar"])
+ #=> '<td class="item foo bar" id="item_1"></td>'
+
+ *Semyon Perepelitsa*
+
+* Remove `BestStandardsSupport` middleware, !DOCTYPE html already triggers
+ standards mode per http://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx
+ and ChromeFrame header has been moved to `config.action_dispatch.default_headers`
+
+ *Guillermo Iguaran*
+
+* Fix CSRF protection and `current_url?` helper to work with HEAD requests
+ now that `ActionDispatch::Head` has been removed in favor of `Rack::Head`.
+
+ *Michiel Sikkes*
+
+* Change `asset_path` to not include `SCRIPT_NAME` when it's used
+ from a mounted engine. Fixes #8119.
+
+ *Piotr Sarnacki*
+
+* Add javascript based routing path matcher to `/rails/info/routes`.
+ Routes can now be filtered by whether or not they match a path.
+
+ *Richard Schneeman*
+
+* Given
+
+ params.permit(:name)
+
+ `:name` passes if it is a key of `params` whose value is a permitted scalar.
+
+ Similarly, given
+
+ params.permit(tags: [])
+
+ `:tags` passes if it is a key of `params` whose value is an array of
+ permitted scalars.
+
+ Permitted scalars filtering happens at any level of nesting.
+
+ *Xavier Noria*
* Change the behavior of route defaults so that explicit defaults are no longer
required where the key is not part of the path. For example:
@@ -197,7 +292,8 @@
* More descriptive error messages when calling `render :partial` with
an invalid `:layout` argument.
- #8376
+
+ Fixes #8376.
render partial: 'partial', layout: true
@@ -492,10 +588,13 @@
*Richard Schneeman*
-* Deprecate availbility of `ActionView::RecordIdentifier` in controllers by default.
- It's view specific and can be easily included in controller manually if someone
- really needs it. RecordIdentifier will be removed from `ActionController::Base`
- in Rails 4.1. *Piotr Sarnacki*
+* Deprecate availability of `ActionView::RecordIdentifier` in controllers by default.
+ It's view specific and can be easily included in controllers manually if someone
+ really needs it. Also deprecate calling `ActionController::RecordIdentifier.dom_id` and
+ `dom_class` directly, in favor of `ActionView::RecordIdentifier.dom_id` and `dom_class`.
+ `RecordIdentifier` will be removed from `ActionController::Base` in Rails 4.1.
+
+ *Piotr Sarnacki*
* Fix `ActionView::RecordIdentifier` to work as a singleton. *Piotr Sarnacki*
@@ -726,7 +825,7 @@
* Removed old text helper apis from `highlight`, `excerpt` and `word_wrap`. *Jeremy Walker*
* Templates without a handler extension now raises a deprecation warning but still
- defaults to ERb. In future releases, it will simply return the template contents. *Steve Klabnik*
+ defaults to ERB. In future releases, it will simply return the template contents. *Steve Klabnik*
* Deprecate `:disable_with` in favor of `data: { disable_with: "Text" }` option from `submit_tag`, `button_tag` and `button_to` helpers.
diff --git a/actionpack/RUNNING_UNIT_TESTS b/actionpack/RUNNING_UNIT_TESTS.rdoc
index 1b29abd2d1..1b29abd2d1 100644
--- a/actionpack/RUNNING_UNIT_TESTS
+++ b/actionpack/RUNNING_UNIT_TESTS.rdoc
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index f00fc46b2e..03eeb841ee 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -21,8 +21,8 @@ Gem::Specification.new do |s|
s.add_dependency 'activesupport', version
s.add_dependency 'builder', '~> 3.1.0'
- s.add_dependency 'rack', '~> 1.4.3'
- s.add_dependency 'rack-test', '~> 0.6.1'
+ s.add_dependency 'rack', '~> 1.5.2'
+ s.add_dependency 'rack-test', '~> 0.6.2'
s.add_dependency 'erubis', '~> 2.7.0'
s.add_development_dependency 'activemodel', version
diff --git a/actionpack/lib/abstract_controller/translation.rb b/actionpack/lib/abstract_controller/translation.rb
index b6c484d188..db48022b9f 100644
--- a/actionpack/lib/abstract_controller/translation.rb
+++ b/actionpack/lib/abstract_controller/translation.rb
@@ -1,5 +1,13 @@
module AbstractController
module Translation
+ # Delegates to <tt>I18n.translate</tt>. Also aliased as <tt>t</tt>.
+ #
+ # When the given key starts with a period, it will be scoped by the current
+ # controller and action. So if you call <tt>translate(".foo")</tt> from
+ # <tt>PeopleController#index</tt>, it will convert the call to
+ # <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
+ # to translate many keys within the same controller / action and gives you a
+ # simple framework for scoping them consistently.
def translate(*args)
key = args.first
if key.is_a?(String) && (key[0] == '.')
@@ -11,6 +19,7 @@ module AbstractController
end
alias :t :translate
+ # Delegates to <tt>I18n.localize</tt>. Also aliased as <tt>l</tt>.
def localize(*args)
I18n.localize(*args)
end
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index cf2cda039d..ea33d975ef 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -82,10 +82,6 @@ module ActionController
end
end
- def caching_allowed?
- request.get? && response.status == 200
- end
-
def view_cache_dependencies
self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
end
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 832dec7b2a..143b3e0cbd 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -1,3 +1,4 @@
+require 'active_support/core_ext/array/extract_options'
require 'action_dispatch/middleware/stack'
module ActionController
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index eddee08545..6e0cd51d8b 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/keys'
+
module ActionController
module ConditionalGet
extend ActiveSupport::Concern
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index d04fbae150..93568da9ef 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -1,3 +1,4 @@
+require 'active_support/core_ext/array/extract_options'
require 'abstract_controller/collector'
module ActionController #:nodoc:
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 59b91a240e..e9031f3fac 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -88,7 +88,7 @@ module ActionController
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
# characters; and is terminated by a colon (":").
# The protocol relative scheme starts with a double slash "//"
- when %r{^(\w[\w+.-]*:|//).*}
+ when %r{\A(\w[\w+.-]*:|//).*}
options
when String
request.protocol + request.host_with_port + options
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index c5db0cb0d4..17379cf7ac 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -102,15 +102,16 @@ module ActionController #:nodoc:
# This is the method that defines the application behavior when a request is found to be unverified.
def handle_unverified_request
- request.session = NullSessionHash.new
+ request.session = NullSessionHash.new(request.env)
request.env['action_dispatch.request.flash_hash'] = nil
request.env['rack.session.options'] = { skip: true }
request.env['action_dispatch.cookies'] = NullCookieJar.build(request)
end
class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
- def initialize
- super(nil, nil)
+ def initialize(env)
+ super(nil, env)
+ @data = {}
@loaded = true
end
@@ -125,7 +126,7 @@ module ActionController #:nodoc:
host = request.host
secure = request.ssl?
- new(key_generator, host, secure)
+ new(key_generator, host, secure, options_for_env({}))
end
def write(*)
@@ -162,11 +163,11 @@ module ActionController #:nodoc:
# Returns true or false if a request is verified. Checks:
#
- # * is it a GET request? Gets should be safe and idempotent
+ # * is it a GET or HEAD request? Gets should be safe and idempotent
# * Does the form_authenticity_token match the given token value from the params?
# * Does the X-CSRF-Token header match the form_authenticity_token
def verified_request?
- !protect_against_forgery? || request.get? ||
+ !protect_against_forgery? || request.get? || request.head? ||
form_authenticity_token == params[request_forgery_protection_token] ||
form_authenticity_token == request.headers['X-CSRF-Token']
end
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index e6e58ce6cd..7e720ca6f5 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -1,6 +1,7 @@
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/array/wrap'
require 'active_support/rescuable'
+require 'action_dispatch/http/upload'
module ActionController
# Raised when a required parameter is missing.
@@ -19,6 +20,20 @@ module ActionController
end
end
+ # Raised when a supplied parameter is not expected.
+ #
+ # params = ActionController::Parameters.new(a: "123", b: "456")
+ # params.permit(:c)
+ # # => ActionController::UnpermittedParameters: found unexpected keys: a, b
+ class UnpermittedParameters < IndexError
+ attr_reader :params # :nodoc:
+
+ def initialize(params) # :nodoc:
+ @params = params
+ super("found unpermitted parameters: #{params.join(", ")}")
+ end
+ end
+
# == Action Controller \Parameters
#
# Allows to choose which attributes should be whitelisted for mass updating
@@ -43,10 +58,15 @@ module ActionController
# Person.first.update!(permitted)
# # => #<Person id: 1, name: "Francesco", age: 22, role: "user">
#
- # It provides a +permit_all_parameters+ option that controls the top-level
- # behavior of new instances. If it's +true+, all the parameters will be
- # permitted by default. The default value for +permit_all_parameters+
- # option is +false+.
+ # It provides two options that controls the top-level behavior of new instances:
+ #
+ # * +permit_all_parameters+ - If it's +true+, all the parameters will be
+ # permitted by default. The default is +false+.
+ # * +action_on_unpermitted_parameters+ - Allow to control the behavior when parameters
+ # that are not explicitly permitted are found. The values can be <tt>:log</tt> to
+ # write a message on the logger or <tt>:raise</tt> to raise
+ # ActionController::UnpermittedParameters exception. The default value is <tt>:log</tt>
+ # in test and development environments, +false+ otherwise.
#
# params = ActionController::Parameters.new
# params.permitted? # => false
@@ -56,6 +76,16 @@ module ActionController
# params = ActionController::Parameters.new
# params.permitted? # => true
#
+ # params = ActionController::Parameters.new(a: "123", b: "456")
+ # params.permit(:c)
+ # # => {}
+ #
+ # ActionController::Parameters.action_on_unpermitted_parameters = :raise
+ #
+ # params = ActionController::Parameters.new(a: "123", b: "456")
+ # params.permit(:c)
+ # # => ActionController::UnpermittedParameters: found unpermitted keys: a, b
+ #
# <tt>ActionController::Parameters</tt> is inherited from
# <tt>ActiveSupport::HashWithIndifferentAccess</tt>, this means
# that you can fetch values using either <tt>:key</tt> or <tt>"key"</tt>.
@@ -65,6 +95,11 @@ module ActionController
# params["key"] # => "value"
class Parameters < ActiveSupport::HashWithIndifferentAccess
cattr_accessor :permit_all_parameters, instance_accessor: false
+ cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
+
+ # Never raise an UnpermittedParameters exception because of these params
+ # are present. They are added by Rails and it's of no concern.
+ NEVER_UNPERMITTED_PARAMS = %w( controller action )
# Returns a new instance of <tt>ActionController::Parameters</tt>.
# Also, sets the +permitted+ attribute to the default value of
@@ -150,6 +185,21 @@ module ActionController
# permitted.has_key?(:age) # => true
# permitted.has_key?(:role) # => false
#
+ # Only permitted scalars pass the filter. For example, given
+ #
+ # params.permit(:name)
+ #
+ # +:name+ passes it is a key of +params+ whose associated value is of type
+ # +String+, +Symbol+, +NilClass+, +Numeric+, +TrueClass+, +FalseClass+,
+ # +Date+, +Time+, +DateTime+, +StringIO+, +IO+, or
+ # +ActionDispatch::Http::UploadedFile+. Otherwise, the key +:name+ is
+ # filtered out.
+ #
+ # You may declare that the parameter should be an array of permitted scalars
+ # by mapping it to an empty array:
+ #
+ # params.permit(tags: [])
+ #
# You can also use +permit+ on nested parameters, like:
#
# params = ActionController::Parameters.new({
@@ -196,32 +246,15 @@ module ActionController
filters.flatten.each do |filter|
case filter
- when Symbol, String then
- if has_key?(filter)
- _value = self[filter]
- params[filter] = _value unless Hash === _value
- end
- keys.grep(/\A#{Regexp.escape(filter)}\(\d+[if]?\)\z/) { |key| params[key] = self[key] }
+ when Symbol, String
+ permitted_scalar_filter(params, filter)
when Hash then
- filter = filter.with_indifferent_access
-
- self.slice(*filter.keys).each do |key, values|
- return unless values
-
- key = key.to_sym
-
- params[key] = each_element(values) do |value|
- # filters are a Hash, so we expect value to be a Hash too
- next if filter.is_a?(Hash) && !value.is_a?(Hash)
-
- value = self.class.new(value) if !value.respond_to?(:permit)
-
- value.permit(*Array.wrap(filter[key]))
- end
- end
+ hash_filter(params, filter)
end
end
+ unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters
+
params.permit!
end
@@ -300,6 +333,99 @@ module ActionController
yield object
end
end
+
+ def unpermitted_parameters!(params)
+ unpermitted_keys = unpermitted_keys(params)
+ if unpermitted_keys.any?
+ case self.class.action_on_unpermitted_parameters
+ when :log
+ ActionController::Base.logger.debug "Unpermitted parameters: #{unpermitted_keys.join(", ")}"
+ when :raise
+ raise ActionController::UnpermittedParameters.new(unpermitted_keys)
+ end
+ end
+ end
+
+ def unpermitted_keys(params)
+ self.keys - params.keys - NEVER_UNPERMITTED_PARAMS
+ end
+
+ #
+ # --- Filtering ----------------------------------------------------------
+ #
+
+ # This is a white list of permitted scalar types that includes the ones
+ # supported in XML and JSON requests.
+ #
+ # This list is in particular used to filter ordinary requests, String goes
+ # as first element to quickly short-circuit the common case.
+ #
+ # If you modify this collection please update the API of +permit+ above.
+ PERMITTED_SCALAR_TYPES = [
+ String,
+ Symbol,
+ NilClass,
+ Numeric,
+ TrueClass,
+ FalseClass,
+ Date,
+ Time,
+ # DateTimes are Dates, we document the type but avoid the redundant check.
+ StringIO,
+ IO,
+ ActionDispatch::Http::UploadedFile,
+ ]
+
+ def permitted_scalar?(value)
+ PERMITTED_SCALAR_TYPES.any? {|type| value.is_a?(type)}
+ end
+
+ def permitted_scalar_filter(params, key)
+ if has_key?(key) && permitted_scalar?(self[key])
+ params[key] = self[key]
+ end
+
+ keys.grep(/\A#{Regexp.escape(key)}\(\d+[if]?\)\z/) do |k|
+ if permitted_scalar?(self[k])
+ params[k] = self[k]
+ end
+ end
+ end
+
+ def array_of_permitted_scalars?(value)
+ if value.is_a?(Array)
+ value.all? {|element| permitted_scalar?(element)}
+ end
+ end
+
+ def array_of_permitted_scalars_filter(params, key)
+ if has_key?(key) && array_of_permitted_scalars?(self[key])
+ params[key] = self[key]
+ end
+ end
+
+ EMPTY_ARRAY = []
+ def hash_filter(params, filter)
+ filter = filter.with_indifferent_access
+
+ # Slicing filters out non-declared keys.
+ slice(*filter.keys).each do |key, value|
+ return unless value
+
+ if filter[key] == EMPTY_ARRAY
+ # Declaration { comment_ids: [] }.
+ array_of_permitted_scalars_filter(params, key)
+ else
+ # Declaration { user: :name } or { user: [:name, :age, { adress: ... }] }.
+ params[key] = each_element(value) do |element|
+ if element.is_a?(Hash)
+ element = self.class.new(element) unless element.respond_to?(:permit)
+ element.permit(*Array.wrap(filter[key]))
+ end
+ end
+ end
+ end
+ end
end
# == Strong \Parameters
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 3e44155f73..5379547c57 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -20,22 +20,27 @@ module ActionController
end
initializer "action_controller.parameters_config" do |app|
- ActionController::Parameters.permit_all_parameters = app.config.action_controller.delete(:permit_all_parameters) { false }
+ options = app.config.action_controller
+
+ ActionController::Parameters.permit_all_parameters = options.delete(:permit_all_parameters) { false }
+ ActionController::Parameters.action_on_unpermitted_parameters = options.delete(:action_on_unpermitted_parameters) do
+ (Rails.env.test? || Rails.env.development?) ? :log : false
+ end
end
initializer "action_controller.set_configs" do |app|
paths = app.config.paths
options = app.config.action_controller
- options.logger ||= Rails.logger
- options.cache_store ||= Rails.cache
+ options.logger ||= Rails.logger
+ options.cache_store ||= Rails.cache
- options.javascripts_dir ||= paths["public/javascripts"].first
- options.stylesheets_dir ||= paths["public/stylesheets"].first
+ options.javascripts_dir ||= paths["public/javascripts"].first
+ options.stylesheets_dir ||= paths["public/stylesheets"].first
# Ensure readers methods get compiled
- options.asset_host ||= app.config.asset_host
- options.relative_url_root ||= app.config.relative_url_root
+ options.asset_host ||= app.config.asset_host
+ options.relative_url_root ||= app.config.relative_url_root
ActiveSupport.on_load(:action_controller) do
include app.routes.mounted_helpers
diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb
index 70e0a820c4..d598bac467 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -2,17 +2,29 @@ require 'action_view/record_identifier'
module ActionController
module RecordIdentifier
- MESSAGE = 'method will no longer be included by default in controllers since Rails 4.1. ' +
- 'If you would like to use it in controllers, please include ' +
- 'ActionView::RecordIdentifier module.'
+ MODULE_MESSAGE = 'Calling ActionController::RecordIdentifier.%s is deprecated and ' \
+ 'will be removed in Rails 4.1, please call using ActionView::RecordIdentifier instead.'
+ INSTANCE_MESSAGE = '%s method will no longer be included by default in controllers ' \
+ 'since Rails 4.1. If you would like to use it in controllers, please include ' \
+ 'ActionView::RecordIdentifier module.'
def dom_id(record, prefix = nil)
- ActiveSupport::Deprecation.warn('dom_id ' + MESSAGE)
+ ActiveSupport::Deprecation.warn(INSTANCE_MESSAGE % 'dom_id')
ActionView::RecordIdentifier.dom_id(record, prefix)
end
def dom_class(record, prefix = nil)
- ActiveSupport::Deprecation.warn('dom_class ' + MESSAGE)
+ ActiveSupport::Deprecation.warn(INSTANCE_MESSAGE % 'dom_class')
+ ActionView::RecordIdentifier.dom_class(record, prefix)
+ end
+
+ def self.dom_id(record, prefix = nil)
+ ActiveSupport::Deprecation.warn(MODULE_MESSAGE % 'dom_id')
+ ActionView::RecordIdentifier.dom_id(record, prefix)
+ end
+
+ def self.dom_class(record, prefix = nil)
+ ActiveSupport::Deprecation.warn(MODULE_MESSAGE % 'dom_class')
ActionView::RecordIdentifier.dom_class(record, prefix)
end
end
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 331d15d403..bba1f1e201 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -1,6 +1,7 @@
require 'rack/session/abstract/id'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/module/anonymous'
+require 'active_support/core_ext/hash/keys'
module ActionController
module TemplateAssertions
@@ -81,8 +82,7 @@ module ActionController
# # assert that the "_customer" partial was rendered with a specific object
# assert_template partial: '_customer', locals: { customer: @customer }
def assert_template(options = {}, message = nil)
- # Force body to be read in case the
- # template is being streamed
+ # Force body to be read in case the template is being streamed.
response.body
case options
@@ -126,7 +126,11 @@ module ActionController
if expected_partial = options[:partial]
if expected_locals = options[:locals]
if defined?(@_rendered_views)
- view = expected_partial.to_s.sub(/^_/,'')
+ view = expected_partial.to_s.sub(/^_/, '').sub(/\/_(?=[^\/]+\z)/, '/')
+
+ partial_was_not_rendered_msg = "expected %s to be rendered but it was not." % view
+ assert_includes @_rendered_views.rendered_views, view, partial_was_not_rendered_msg
+
msg = 'expecting %s to be rendered with %s but was with %s' % [expected_partial,
expected_locals,
@_rendered_views.locals_for(view)]
@@ -236,18 +240,39 @@ module ActionController
end
end
+ # Methods #destroy and #load! are overridden to avoid calling methods on the
+ # @store object, which does not exist for the TestSession class.
class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
def initialize(session = {})
super(nil, nil)
- replace(session.stringify_keys)
+ @id = SecureRandom.hex(16)
+ @data = stringify_keys(session)
@loaded = true
end
def exists?
true
end
+
+ def keys
+ @data.keys
+ end
+
+ def values
+ @data.values
+ end
+
+ def destroy
+ clear
+ end
+
+ private
+
+ def load!
+ @id
+ end
end
# Superclass for ActionController functional tests. Functional tests allow you to
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 9ab048b756..618e2f3033 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -47,7 +47,6 @@ module ActionDispatch
autoload_under 'middleware' do
autoload :RequestId
- autoload :BestStandardsSupport
autoload :Callbacks
autoload :Cookies
autoload :DebugExceptions
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 57660e93c4..89a7b12818 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -122,7 +122,7 @@ module ActionDispatch
def valid_accept_header
(xhr? && (accept || content_mime_type)) ||
- (accept && accept !~ BROWSER_LIKE_ACCEPTS)
+ (accept.present? && accept !~ BROWSER_LIKE_ACCEPTS)
end
def use_accept_header
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index 25edd196c3..446862aad0 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -55,7 +55,7 @@ module ActionDispatch
# should really prevent that from happening
def encode_params(params)
if params.is_a?(String)
- return params.force_encoding("UTF-8").encode!
+ return params.force_encoding(Encoding::UTF_8).encode!
elsif !params.is_a?(Hash)
return params
end
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 91cf4784db..ca1ace4537 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -137,6 +137,7 @@ module ActionDispatch # :nodoc:
@committed
end
+ # Sets the HTTP status code.
def status=(status)
@status = Rack::Utils.status_code(status)
end
@@ -145,23 +146,31 @@ module ActionDispatch # :nodoc:
@content_type = content_type.to_s
end
- # The response code of the request
+ # The response code of the request.
def response_code
@status
end
- # Returns a String to ensure compatibility with Net::HTTPResponse
+ # Returns a string to ensure compatibility with <tt>Net::HTTPResponse</tt>.
def code
@status.to_s
end
+ # Returns the corresponding message for the current HTTP status code:
+ #
+ # response.status = 200
+ # response.message # => "OK"
+ #
+ # response.status = 404
+ # response.message # => "Not Found"
+ #
def message
Rack::Utils::HTTP_STATUS_CODES[@status]
end
alias_method :status_message, :message
def respond_to?(method)
- if method.to_sym == :to_path
+ if method.to_s == 'to_path'
stream.respond_to?(:to_path)
else
super
@@ -172,6 +181,8 @@ module ActionDispatch # :nodoc:
stream.to_path
end
+ # Returns the content of the response as a string. This contains the contents
+ # of any calls to <tt>render</tt>.
def body
strings = []
each { |part| strings << part.to_s }
@@ -180,6 +191,7 @@ module ActionDispatch # :nodoc:
EMPTY = " "
+ # Allows you to manually set or override the response body.
def body=(body)
@blank = true if body == EMPTY
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index 8a97248eb3..67cb7fbcb5 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -70,7 +70,7 @@ module ActionDispatch
def encode_filename(filename)
# Encode the filename in the utf8 encoding, unless it is nil
- filename.force_encoding("UTF-8").encode! if filename
+ filename.force_encoding(Encoding::UTF_8).encode! if filename
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/best_standards_support.rb b/actionpack/lib/action_dispatch/middleware/best_standards_support.rb
deleted file mode 100644
index 94efeb79fa..0000000000
--- a/actionpack/lib/action_dispatch/middleware/best_standards_support.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-module ActionDispatch
- class BestStandardsSupport
- def initialize(app, type = true)
- @app = app
-
- @header = case type
- when true
- "IE=Edge,chrome=1"
- when :builtin
- "IE=Edge"
- when false
- nil
- end
- end
-
- def call(env)
- status, headers, body = @app.call(env)
-
- if headers["X-UA-Compatible"] && @header
- unless headers["X-UA-Compatible"][@header]
- headers["X-UA-Compatible"] << "," << @header.to_s
- end
- else
- headers["X-UA-Compatible"] = @header
- end
-
- [status, headers, body]
- end
- end
-end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 6ecbb03784..fff26bd1b2 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/module/attribute_accessors'
+require 'active_support/key_generator'
require 'active_support/message_verifier'
module ActionDispatch
@@ -110,13 +111,17 @@ module ActionDispatch
# $& => example.local
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
+ def self.options_for_env(env) #:nodoc:
+ { signed_cookie_salt: env[SIGNED_COOKIE_SALT] || '',
+ encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT] || '',
+ encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT] || '',
+ token_key: env[TOKEN_KEY] }
+ end
+
def self.build(request)
env = request.env
key_generator = env[GENERATOR_KEY]
- options = { signed_cookie_salt: env[SIGNED_COOKIE_SALT],
- encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT],
- encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT],
- token_key: env[TOKEN_KEY] }
+ options = options_for_env env
host = request.host
secure = request.ssl?
@@ -145,6 +150,10 @@ module ActionDispatch
@cookies[name.to_s]
end
+ def fetch(name, *args, &block)
+ @cookies.fetch(name.to_s, *args, &block)
+ end
+
def key?(name)
@cookies.key?(name.to_s)
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index 7c12590c49..84df55fd5a 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -26,7 +26,7 @@ module ActionDispatch
def generate_sid
sid = SecureRandom.hex(16)
- sid.encode!('UTF-8')
+ sid.encode!(Encoding::UTF_8)
sid
end
diff --git a/actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb b/actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb
index 400ae97d22..24e44f31ac 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb
@@ -7,7 +7,7 @@
<td data-route-verb='<%= route[:verb] %>'>
<%= route[:verb] %>
</td>
- <td data-route-path='<%= route[:path] %>'>
+ <td data-route-path='<%= route[:path] %>' data-regexp='<%= route[:regexp] %>'>
<%= route[:path] %>
</td>
<td data-route-reqs='<%= route[:reqs] %>'>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
index 9026c4eeb2..95461fa693 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
@@ -1,22 +1,58 @@
<% content_for :style do %>
- #route_table td { padding: 0 30px; }
- #route_table { margin: 0 auto 0; }
+ #route_table {
+ margin: 0 auto 0;
+ border-collapse: collapse;
+ }
+
+ #route_table td {
+ padding: 0 30px;
+ }
+
+ #route_table tr.bottom th {
+ padding-bottom: 10px;
+ line-height: 15px;
+ }
+
+ #route_table .matched_paths {
+ background-color: LightGoldenRodYellow;
+ }
+
+ #route_table .matched_paths {
+ border-bottom: solid 3px SlateGrey;
+ }
+
+ #path_search {
+ width: 80%;
+ font-size: inherit;
+ }
<% end %>
<table id='route_table' class='route_table'>
<thead>
<tr>
- <th>Helper<br />
+ <th>Helper</th>
+ <th>HTTP Verb</th>
+ <th>Path</th>
+ <th>Controller#Action</th>
+ </tr>
+ <tr class='bottom'>
+ <th><%# Helper %>
<%= link_to "Path", "#", 'data-route-helper' => '_path',
title: "Returns a relative path (without the http or domain)" %> /
<%= link_to "Url", "#", 'data-route-helper' => '_url',
- title: "Returns an absolute url (with the http and domain)" %>
+ title: "Returns an absolute url (with the http and domain)" %>
+ </th>
+ <th><%# HTTP Verb %>
+ </th>
+ <th><%# Path %>
+ <%= search_field(:path, nil, id: 'path_search', placeholder: "Path Match") %>
+ </th>
+ <th><%# Controller#action %>
</th>
- <th>HTTP Verb</th>
- <th>Path</th>
- <th>Controller#Action</th>
</tr>
</thead>
+ <tbody class='matched_paths' id='matched_paths'>
+ </tbody>
<tbody>
<%= yield %>
</tbody>
@@ -25,7 +61,7 @@
<script type='text/javascript'>
function each(elems, func) {
if (!elems instanceof Array) { elems = [elems]; }
- for (var i = elems.length; i--; ) {
+ for (var i = 0, len = elems.length; i < len; i++) {
func(elems[i]);
}
}
@@ -46,11 +82,63 @@
function setupRouteToggleHelperLinks() {
var toggleLinks = document.querySelectorAll('#route_table [data-route-helper]');
onClick(toggleLinks, function(){
- var helperTxt = this.getAttribute("data-route-helper");
- var helperElems = document.querySelectorAll('[data-route-name] span.helper');
+ var helperTxt = this.getAttribute("data-route-helper"),
+ helperElems = document.querySelectorAll('[data-route-name] span.helper');
setValOn(helperElems, helperTxt);
});
}
+ // takes an array of elements with a data-regexp attribute and
+ // passes their their parent <tr> into the callback function
+ // if the regexp matchs a given path
+ function eachElemsForPath(elems, path, func) {
+ each(elems, function(e){
+ var reg = e.getAttribute("data-regexp");
+ if (path.match(RegExp(reg))) {
+ func(e.parentNode.cloneNode(true));
+ }
+ })
+ }
+
+ // Ensure path always starts with a slash "/" and remove params or fragments
+ function sanitizePath(path) {
+ var path = path.charAt(0) == '/' ? path : "/" + path;
+ return path.replace(/\#.*|\?.*/, '');
+ }
+
+ // Enables path search functionality
+ function setupMatchPaths() {
+ var regexpElems = document.querySelectorAll('#route_table [data-regexp]'),
+ pathElem = document.querySelector('#path_search'),
+ selectedSection = document.querySelector('#matched_paths'),
+ noMatchText = '<tr><th colspan="4">None</th></tr>';
+
+
+ // Remove matches if no path is present
+ pathElem.onblur = function(e) {
+ if (pathElem.value === "") selectedSection.innerHTML = "";
+ }
+
+ // On key press perform a search for matching paths
+ pathElem.onkeyup = function(e){
+ var path = sanitizePath(pathElem.value),
+ defaultText = '<tr><th colspan="4">Paths Matching (' + path + '):</th></tr>';
+
+ // Clear out results section
+ selectedSection.innerHTML= defaultText;
+
+ // Display matches if they exist
+ eachElemsForPath(regexpElems, path, function(e){
+ selectedSection.appendChild(e);
+ });
+
+ // If no match present, tell the user
+ if (selectedSection.innerHTML === defaultText) {
+ selectedSection.innerHTML = selectedSection.innerHTML + noMatchText;
+ }
+ }
+ }
+
+ setupMatchPaths();
setupRouteToggleHelperLinks();
</script>
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index 5a835ae439..edf37bb9a5 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -6,7 +6,6 @@ module ActionDispatch
config.action_dispatch.x_sendfile_header = nil
config.action_dispatch.ip_spoofing_check = true
config.action_dispatch.show_exceptions = true
- config.action_dispatch.best_standards_support = true
config.action_dispatch.tld_length = 1
config.action_dispatch.ignore_accept_header = false
config.action_dispatch.rescue_templates = { }
@@ -21,7 +20,8 @@ module ActionDispatch
config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN',
'X-XSS-Protection' => '1; mode=block',
- 'X-Content-Type-Options' => 'nosniff'
+ 'X-Content-Type-Options' => 'nosniff',
+ 'X-UA-Compatible' => 'chrome=1'
}
config.eager_load_namespaces << ActionDispatch
diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb
index a05a23d953..7bc812fd22 100644
--- a/actionpack/lib/action_dispatch/request/session.rb
+++ b/actionpack/lib/action_dispatch/request/session.rb
@@ -63,6 +63,10 @@ module ActionDispatch
@exists = nil # we haven't checked yet
end
+ def id
+ options[:id]
+ end
+
def options
Options.find @env
end
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
index ea3e8357d4..bc6dd7145c 100644
--- a/actionpack/lib/action_dispatch/routing/inspector.rb
+++ b/actionpack/lib/action_dispatch/routing/inspector.rb
@@ -34,6 +34,23 @@ module ActionDispatch
super.to_s
end
+ def regexp
+ __getobj__.path.to_regexp
+ end
+
+ def json_regexp
+ str = regexp.inspect.
+ sub('\\A' , '^').
+ sub('\\Z' , '$').
+ sub('\\z' , '$').
+ sub(/^\// , '').
+ sub(/\/[a-z]*$/ , '').
+ gsub(/\(\?#.+\)/ , '').
+ gsub(/\(\?-\w+:/ , '(').
+ gsub(/\s/ , '')
+ Regexp.new(str).source
+ end
+
def reqs
@reqs ||= begin
reqs = endpoint
@@ -73,10 +90,11 @@ module ActionDispatch
routes_to_display = filter_routes(filter)
routes = collect_routes(routes_to_display)
- formatter.section :application, 'Application routes', routes
+ formatter.section routes
@engines.each do |name, engine_routes|
- formatter.section :engine, "Routes for #{name}", engine_routes
+ formatter.section_title "Routes for #{name}"
+ formatter.section engine_routes
end
formatter.result
@@ -100,7 +118,11 @@ module ActionDispatch
end.collect do |route|
collect_engine_routes(route)
- { name: route.name, verb: route.verb, path: route.path, reqs: route.reqs }
+ { name: route.name,
+ verb: route.verb,
+ path: route.path,
+ reqs: route.reqs,
+ regexp: route.json_regexp }
end
end
@@ -125,8 +147,11 @@ module ActionDispatch
@buffer.join("\n")
end
- def section(type, title, routes)
- @buffer << "\n#{title}:" unless type == :application
+ def section_title(title)
+ @buffer << "\n#{title}:"
+ end
+
+ def section(routes)
@buffer << draw_section(routes)
end
@@ -148,8 +173,11 @@ module ActionDispatch
@buffer = []
end
- def section(type, title, routes)
+ def section_title(title)
@buffer << %(<tr><th colspan="4">#{title}</th></tr>)
+ end
+
+ def section(routes)
@buffer << @view.render(partial: "routes/route", collection: routes)
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 6d93f609a6..0a41ed0fcf 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -2,6 +2,7 @@ require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/enumerable'
+require 'active_support/core_ext/array/extract_options'
require 'active_support/inflector'
require 'action_dispatch/routing/redirection'
@@ -245,6 +246,12 @@ module ActionDispatch
raise ArgumentError, "missing :action"
end
+ if controller.is_a?(String) && controller !~ /\A[a-z_0-9\/]*\z/
+ message = "'#{controller}' is not a supported controller name. This can lead to potential routing problems."
+ message << " See http://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use"
+ raise ArgumentError, message
+ end
+
hash = {}
hash[:controller] = controller unless controller.blank?
hash[:action] = action unless action.blank?
@@ -1403,9 +1410,10 @@ module ActionDispatch
def add_route(action, options) # :nodoc:
path = path_for_action(action, options.delete(:path))
+ action = action.to_s.dup
- if action.to_s =~ /^[\w\/]+$/
- options[:action] ||= action unless action.to_s.include?("/")
+ if action =~ /^[\w\/]+$/
+ options[:action] ||= action unless action.include?("/")
else
action = nil
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index c72310cca3..ff86f87d49 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -4,6 +4,7 @@ require 'thread_safe'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/module/remove_method'
+require 'active_support/core_ext/array/extract_options'
require 'action_controller/metal/exceptions'
module ActionDispatch
@@ -100,36 +101,16 @@ module ActionDispatch
def initialize
@routes = {}
@helpers = []
- @module = Module.new do
- protected
-
- def handle_positional_args(args, options, segment_keys)
- inner_options = args.extract_options!
- result = options.dup
-
- if args.size > 0
- keys = segment_keys
- if args.size < keys.size - 1 # take format into account
- keys -= self.url_options.keys if self.respond_to?(:url_options)
- keys -= options.keys
- end
- result.merge!(Hash[keys.zip(args)])
- end
-
- result.merge!(inner_options)
- end
- end
+ @module = Module.new
end
def helper_names
- self.module.instance_methods.map(&:to_s)
+ @helpers.map(&:to_s)
end
def clear!
@helpers.each do |helper|
- @module.module_eval do
- remove_possible_method helper
- end
+ @module.remove_possible_method helper
end
@routes.clear
@@ -162,68 +143,126 @@ module ActionDispatch
routes.length
end
- private
+ class UrlHelper # :nodoc:
+ def self.create(route, options)
+ if optimize_helper?(route)
+ OptimizedUrlHelper.new(route, options)
+ else
+ new route, options
+ end
+ end
- def define_named_route_methods(name, route)
- define_url_helper route, :"#{name}_path",
- route.defaults.merge(:use_route => name, :only_path => true)
- define_url_helper route, :"#{name}_url",
- route.defaults.merge(:use_route => name, :only_path => false)
+ def self.optimize_helper?(route)
+ route.requirements.except(:controller, :action).empty?
end
- # Create a url helper allowing ordered parameters to be associated
- # with corresponding dynamic segments, so you can do:
- #
- # foo_url(bar, baz, bang)
- #
- # Instead of:
- #
- # foo_url(bar: bar, baz: baz, bang: bang)
- #
- # Also allow options hash, so you can do:
- #
- # foo_url(bar, baz, bang, sort_by: 'baz')
- #
- def define_url_helper(route, name, options)
- @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
- remove_possible_method :#{name}
- def #{name}(*args)
- if #{optimize_helper?(route)} && args.size == #{route.required_parts.size} && !args.last.is_a?(Hash) && optimize_routes_generation?
- options = #{options.inspect}
- options.merge!(url_options) if respond_to?(:url_options)
- options[:path] = "#{optimized_helper(route)}"
- ActionDispatch::Http::URL.url_for(options)
- else
- url_for(handle_positional_args(args, #{options.inspect}, #{route.segment_keys.inspect}))
- end
+ class OptimizedUrlHelper < UrlHelper # :nodoc:
+ attr_reader :arg_size
+
+ def initialize(route, options)
+ super
+ @path_parts = @route.required_parts
+ @arg_size = @path_parts.size
+ @string_route = string_route(route)
+ end
+
+ def call(t, args)
+ if args.size == arg_size && !args.last.is_a?(Hash) && optimize_routes_generation?(t)
+ @options.merge!(t.url_options) if t.respond_to?(:url_options)
+ @options[:path] = optimized_helper(args)
+ ActionDispatch::Http::URL.url_for(@options)
+ else
+ super
end
- END_EVAL
+ end
+
+ private
+
+ def string_route(route)
+ string_route = route.ast.to_s.dup
+ while string_route.gsub!(/\([^\)]*\)/, "")
+ true
+ end
+ string_route
+ end
+
+ def optimized_helper(args)
+ path = @string_route.dup
+ klass = Journey::Router::Utils
- helpers << name
+ @path_parts.zip(args) do |part, arg|
+ # Replace each route parameter
+ # e.g. :id for regular parameter or *path for globbing
+ # with ruby string interpolation code
+ path.gsub!(/(\*|:)#{part}/, klass.escape_fragment(arg.to_param))
+ end
+ path
+ end
+
+ def optimize_routes_generation?(t)
+ t.send(:optimize_routes_generation?)
+ end
end
- # Clause check about when we need to generate an optimized helper.
- def optimize_helper?(route) #:nodoc:
- route.requirements.except(:controller, :action).empty?
+ def initialize(route, options)
+ @options = options
+ @segment_keys = route.segment_keys
+ @route = route
+ end
+
+ def call(t, args)
+ t.url_for(handle_positional_args(t, args, @options, @segment_keys))
end
- # Generates the interpolation to be used in the optimized helper.
- def optimized_helper(route)
- string_route = route.ast.to_s
+ def handle_positional_args(t, args, options, keys)
+ inner_options = args.extract_options!
+ result = options.dup
- while string_route.gsub!(/\([^\)]*\)/, "")
- true
+ if args.size > 0
+ if args.size < keys.size - 1 # take format into account
+ keys -= t.url_options.keys if t.respond_to?(:url_options)
+ keys -= options.keys
+ end
+ result.merge!(Hash[keys.zip(args)])
end
- route.required_parts.each_with_index do |part, i|
- # Replace each route parameter
- # e.g. :id for regular parameter or *path for globbing
- # with ruby string interpolation code
- string_route.gsub!(/(\*|:)#{part}/, "\#{Journey::Router::Utils.escape_fragment(args[#{i}].to_param)}")
- end
+ result.merge!(inner_options)
+ end
+ end
+
+ private
+ # Create a url helper allowing ordered parameters to be associated
+ # with corresponding dynamic segments, so you can do:
+ #
+ # foo_url(bar, baz, bang)
+ #
+ # Instead of:
+ #
+ # foo_url(bar: bar, baz: baz, bang: bang)
+ #
+ # Also allow options hash, so you can do:
+ #
+ # foo_url(bar, baz, bang, sort_by: 'baz')
+ #
+ def define_url_helper(route, name, options)
+ helper = UrlHelper.create(route, options.dup)
- string_route
+ @module.remove_possible_method name
+ @module.module_eval do
+ define_method(name) do |*args|
+ helper.call self, args
+ end
end
+
+ helpers << name
+ end
+
+ def define_named_route_methods(name, route)
+ define_url_helper route, :"#{name}_path",
+ route.defaults.merge(:use_route => name, :only_path => true)
+ define_url_helper route, :"#{name}_url",
+ route.defaults.merge(:use_route => name, :only_path => false)
+ end
end
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
diff --git a/actionpack/lib/action_dispatch/routing/routes_proxy.rb b/actionpack/lib/action_dispatch/routing/routes_proxy.rb
index 73af5920ed..e2393d3799 100644
--- a/actionpack/lib/action_dispatch/routing/routes_proxy.rb
+++ b/actionpack/lib/action_dispatch/routing/routes_proxy.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/extract_options'
+
module ActionDispatch
module Routing
class RoutesProxy #:nodoc:
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 11743e36f2..31e37893c6 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -182,6 +182,8 @@ module ActionView
# width="30" and height="45", and "50" becomes width="50" and height="50".
# <tt>:size</tt> will be ignored if the value is not in the correct format.
#
+ # ==== Examples
+ #
# image_tag("icon")
# # => <img alt="Icon" src="/assets/icon" />
# image_tag("icon.png")
@@ -212,10 +214,24 @@ module ActionView
end
# Returns a string suitable for an html image tag alt attribute.
- # +src+ is meant to be an image file path.
- # It removes the basename of the file path and the digest, if any.
+ # The +src+ argument is meant to be an image file path.
+ # The method removes the basename of the file path and the digest,
+ # if any. It also removes hyphens and underscores from file names and
+ # replaces them with spaces, returning a space-separated, titleized
+ # string.
+ #
+ # ==== Examples
+ #
+ # image_tag('rails.png')
+ # # => <img alt="Rails" src="/assets/rails.png" />
+ #
+ # image_tag('hyphenated-file-name.png')
+ # # => <img alt="Hyphenated file name" src="/assets/hyphenated-file-name.png" />
+ #
+ # image_tag('underscored_file_name.png')
+ # # => <img alt="Underscored file name" src="/assets/underscored_file_name.png" />
def image_alt(src)
- File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').capitalize
+ File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').tr('-_', ' ').capitalize
end
# Returns an html video tag for the +sources+. If +sources+ is a string,
diff --git a/actionpack/lib/action_view/helpers/asset_url_helper.rb b/actionpack/lib/action_view/helpers/asset_url_helper.rb
index 0affac41e8..71b78cf0b5 100644
--- a/actionpack/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_url_helper.rb
@@ -132,8 +132,7 @@ module ActionView
source = compute_asset_path(source, options)
end
- relative_url_root = (defined?(config.relative_url_root) && config.relative_url_root) ||
- (respond_to?(:request) && request.try(:script_name))
+ relative_url_root = defined?(config.relative_url_root) && config.relative_url_root
if relative_url_root
source = "#{relative_url_root}#{source}" unless source.starts_with?("#{relative_url_root}/")
end
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index cf978d8e83..61f939bff1 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -1,5 +1,6 @@
require 'date'
require 'action_view/helpers/tag_helper'
+require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/date/conversions'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/object/with_options'
@@ -880,6 +881,7 @@ module ActionView
def translated_date_order
date_order = I18n.translate(:'date.order', :locale => @options[:locale], :default => [])
+ date_order = date_order.map { |element| element.to_sym }
forbidden_elements = date_order - [:year, :month, :day]
if forbidden_elements.any?
@@ -1039,14 +1041,38 @@ module ActionView
end
class FormBuilder
+ # Wraps ActionView::Helpers::DateHelper#date_select for form builders:
+ #
+ # <%= form_for @person do |f| %>
+ # <%= f.date_select :birth_date %>
+ # <%= f.submit %>
+ # <% end %>
+ #
+ # Please refer to the documentation of the base helper for details.
def date_select(method, options = {}, html_options = {})
@template.date_select(@object_name, method, objectify_options(options), html_options)
end
+ # Wraps ActionView::Helpers::DateHelper#time_select for form builders:
+ #
+ # <%= form_for @race do |f| %>
+ # <%= f.time_select :average_lap %>
+ # <%= f.submit %>
+ # <% end %>
+ #
+ # Please refer to the documentation of the base helper for details.
def time_select(method, options = {}, html_options = {})
@template.time_select(@object_name, method, objectify_options(options), html_options)
end
+ # Wraps ActionView::Helpers::DateHelper#datetime_select for form builders:
+ #
+ # <%= form_for @person do |f| %>
+ # <%= f.time_select :last_request_at %>
+ # <%= f.submit %>
+ # <% end %>
+ #
+ # Please refer to the documentation of the base helper for details.
def datetime_select(method, options = {}, html_options = {})
@template.datetime_select(@object_name, method, objectify_options(options), html_options)
end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 4a31de715d..3dae1fc87a 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -8,7 +8,6 @@ require 'action_view/model_naming'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/string/output_safety'
-require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/string/inflections'
module ActionView
@@ -771,8 +770,8 @@ module ActionView
# text_field(:post, :title, class: "create_input")
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
#
- # text_field(:session, :user, onchange: "if $('#session_user').value == 'admin' { alert('Your login can not be admin!'); }")
- # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange = "if $('#session_user').value == 'admin' { alert('Your login can not be admin!'); }"/>
+ # text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login can not be admin!'); }")
+ # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login can not be admin!'); }"/>
#
# text_field(:snippet, :code, size: 20, class: 'code_input')
# # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
@@ -792,8 +791,8 @@ module ActionView
# password_field(:account, :secret, class: "form_input", value: @account.secret)
# # => <input type="password" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
#
- # password_field(:user, :password, onchange: "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }")
- # # => <input type="password" id="user_password" name="user[password]" onchange = "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }"/>
+ # password_field(:user, :password, onchange: "if ($('#user_password').val().length > 30) { alert('Your password needs to be shorter!'); }")
+ # # => <input type="password" id="user_password" name="user[password]" onchange="if ($('#user_password').val().length > 30) { alert('Your password needs to be shorter!'); }"/>
#
# password_field(:account, :pin, size: 20, class: 'form_input')
# # => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" />
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index ae7bfd1ec6..8b3a37a853 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -2,6 +2,7 @@ require 'cgi'
require 'erb'
require 'action_view/helpers/form_helper'
require 'active_support/core_ext/string/output_safety'
+require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/array/wrap'
module ActionView
@@ -756,26 +757,74 @@ module ActionView
end
class FormBuilder
+ # Wraps ActionView::Helpers::FormOptionsHelper#select for form builders:
+ #
+ # <%= form_for @post do |f| %>
+ # <%= f.select :person_id, Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true }) %>
+ # <%= f.submit %>
+ # <% end %>
+ #
+ # Please refer to the documentation of the base helper for details.
def select(method, choices, options = {}, html_options = {})
@template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options))
end
+ # Wraps ActionView::Helpers::FormOptionsHelper#collection_select for form builders:
+ #
+ # <%= form_for @post do |f| %>
+ # <%= f.collection_select :person_id, Author.all, :id, :name_with_initial, prompt: true %>
+ # <%= f.submit %>
+ # <% end %>
+ #
+ # Please refer to the documentation of the base helper for details.
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
@template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
end
+ # Wraps ActionView::Helpers::FormOptionsHelper#grouped_collection_select for form builders:
+ #
+ # <%= form_for @city do |f| %>
+ # <%= f.grouped_collection_select :country_id, :country_id, @continents, :countries, :name, :id, :name %>
+ # <%= f.submit %>
+ # <% end %>
+ #
+ # Please refer to the documentation of the base helper for details.
def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
@template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options))
end
+ # Wraps ActionView::Helpers::FormOptionsHelper#time_zone_select for form builders:
+ #
+ # <%= form_for @user do |f| %>
+ # <%= f.time_zone_select :time_zone, nil, include_blank: true %>
+ # <%= f.submit %>
+ # <% end %>
+ #
+ # Please refer to the documentation of the base helper for details.
def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
@template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
end
+ # Wraps ActionView::Helpers::FormOptionsHelper#collection_check_boxes for form builders:
+ #
+ # <%= form_for @post do |f| %>
+ # <%= f.collection_check_boxes :author_ids, Author.all, :id, :name_with_initial %>
+ # <%= f.submit %>
+ # <% end %>
+ #
+ # Please refer to the documentation of the base helper for details.
def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
@template.collection_check_boxes(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options), &block)
end
+ # Wraps ActionView::Helpers::FormOptionsHelper#collection_radio_buttons for form builders:
+ #
+ # <%= form_for @post do |f| %>
+ # <%= f.collection_radio_buttons :author_id, Author.all, :id, :name_with_initial %>
+ # <%= f.submit %>
+ # <% end %>
+ #
+ # Please refer to the documentation of the base helper for details.
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
@template.collection_radio_buttons(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options), &block)
end
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index cfdd7c77d8..878d3e0eda 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -13,8 +13,8 @@ module ActionView
"'" => "\\'"
}
- JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '&#x2028;'
- JS_ESCAPE_MAP["\342\200\251".force_encoding('UTF-8').encode!] = '&#x2029;'
+ JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] = '&#x2028;'
+ JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = '&#x2029;'
# Escapes carriage returns and single and double quotes for JavaScript segments.
#
diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb
index 271a194913..f767957fa9 100644
--- a/actionpack/lib/action_view/helpers/record_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb
@@ -92,7 +92,7 @@ module ActionView
# for each record.
def content_tag_for_single_record(tag_name, record, prefix, options, &block)
options = options ? options.dup : {}
- options[:class] = "#{dom_class(record, prefix)} #{options[:class]}".rstrip
+ options[:class] = [ dom_class(record, prefix), options[:class] ].compact
options[:id] = dom_id(record, prefix)
if block_given?
diff --git a/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb
index d27df45b5a..9655008fe2 100644
--- a/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb
+++ b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb
@@ -13,13 +13,13 @@ module ActionView
end
end
- def render
+ def render(&block)
rendered_collection = render_collection do |item, value, text, default_html_options|
default_html_options[:multiple] = true
builder = instantiate_builder(CheckBoxBuilder, item, value, text, default_html_options)
if block_given?
- yield builder
+ @template_object.capture(builder, &block)
else
render_component(builder)
end
diff --git a/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb
index 81f2ecb2b3..893f4411e7 100644
--- a/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb
+++ b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb
@@ -13,12 +13,12 @@ module ActionView
end
end
- def render
+ def render(&block)
render_collection do |item, value, text, default_html_options|
builder = instantiate_builder(RadioButtonBuilder, item, value, text, default_html_options)
if block_given?
- yield builder
+ @template_object.capture(builder, &block)
else
render_component(builder)
end
diff --git a/actionpack/lib/action_view/helpers/tags/date_select.rb b/actionpack/lib/action_view/helpers/tags/date_select.rb
index 380d6d3686..734591394b 100644
--- a/actionpack/lib/action_view/helpers/tags/date_select.rb
+++ b/actionpack/lib/action_view/helpers/tags/date_select.rb
@@ -27,7 +27,7 @@ module ActionView
end
def datetime_selector(options, html_options)
- datetime = options[:selected] || value(object) || default_datetime(options)
+ datetime = options.fetch(:selected) { value(object) || default_datetime(options) }
@auto_index ||= nil
options = options.dup
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index bade121d44..5e20b557d8 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -514,7 +514,7 @@ module ActionView
"in a #request method"
end
- return false unless request.get?
+ return false unless request.get? || request.head?
url_string = url_for(options)
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index b927c69260..f73d14c79b 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -80,8 +80,7 @@ module ActionView
# problems with converting the user's data to
# the <tt>default_internal</tt>.
#
- # To do so, simply raise the raise +WrongEncodingError+
- # as follows:
+ # To do so, simply raise +WrongEncodingError+ as follows:
#
# raise WrongEncodingError.new(
# problematic_string,
diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb
index b479f991bc..a89d51221e 100644
--- a/actionpack/lib/action_view/template/error.rb
+++ b/actionpack/lib/action_view/template/error.rb
@@ -17,7 +17,7 @@ module ActionView
end
def message
- @string.force_encoding("BINARY")
+ @string.force_encoding(Encoding::ASCII_8BIT)
"Your template was not saved as valid #{@encoding}. Please " \
"either specify #{@encoding} as the encoding for your template " \
"in your text editor, or mark the template with its " \
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index afbbece90f..5aaafc15c1 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -81,7 +81,7 @@ module ActionView
# wrong, we can still find an encoding tag
# (<%# encoding %>) inside the String using a regular
# expression
- template_source = template.source.dup.force_encoding("BINARY")
+ template_source = template.source.dup.force_encoding(Encoding::ASCII_8BIT)
erb = template_source.gsub(ENCODING_TAG, '')
encoding = $2
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 4479da5bc4..463f192d0c 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -122,7 +122,7 @@ module ActionView
class RenderedViewsCollection
def initialize
- @rendered_views ||= {}
+ @rendered_views ||= Hash.new { |hash, key| hash[key] = [] }
end
def add(view, locals)
@@ -134,6 +134,10 @@ module ActionView
@rendered_views[view]
end
+ def rendered_views
+ @rendered_views.keys
+ end
+
def view_rendered?(view, expected_locals)
locals_for(view).any? do |actual_locals|
expected_locals.all? {|key, value| value == actual_locals[key] }
diff --git a/actionpack/test/abstract/translation_test.rb b/actionpack/test/abstract/translation_test.rb
index 99064a8b87..4fdc480b43 100644
--- a/actionpack/test/abstract/translation_test.rb
+++ b/actionpack/test/abstract/translation_test.rb
@@ -1,39 +1,50 @@
require 'abstract_unit'
-# class TranslatingController < ActionController::Base
-# end
-
-class TranslationControllerTest < ActiveSupport::TestCase
- def setup
- @controller = ActionController::Base.new
- end
-
- def test_action_controller_base_responds_to_translate
- assert_respond_to @controller, :translate
- end
-
- def test_action_controller_base_responds_to_t
- assert_respond_to @controller, :t
- end
-
- def test_action_controller_base_responds_to_localize
- assert_respond_to @controller, :localize
- end
-
- def test_action_controller_base_responds_to_l
- assert_respond_to @controller, :l
- end
-
- def test_lazy_lookup
- expected = 'bar'
- @controller.stubs(:action_name => :index)
- I18n.stubs(:translate).with('action_controller.base.index.foo').returns(expected)
- assert_equal expected, @controller.t('.foo')
- end
-
- def test_default_translation
- key, expected = 'one.two' 'bar'
- I18n.stubs(:translate).with(key).returns(expected)
- assert_equal expected, @controller.t(key)
+module AbstractController
+ module Testing
+ class TranslationController < AbstractController::Base
+ include AbstractController::Translation
+ end
+
+ class TranslationControllerTest < ActiveSupport::TestCase
+ def setup
+ @controller = TranslationController.new
+ end
+
+ def test_action_controller_base_responds_to_translate
+ assert_respond_to @controller, :translate
+ end
+
+ def test_action_controller_base_responds_to_t
+ assert_respond_to @controller, :t
+ end
+
+ def test_action_controller_base_responds_to_localize
+ assert_respond_to @controller, :localize
+ end
+
+ def test_action_controller_base_responds_to_l
+ assert_respond_to @controller, :l
+ end
+
+ def test_lazy_lookup
+ expected = 'bar'
+ @controller.stubs(action_name: :index)
+ I18n.stubs(:translate).with('abstract_controller.testing.translation.index.foo').returns(expected)
+ assert_equal expected, @controller.t('.foo')
+ end
+
+ def test_default_translation
+ key, expected = 'one.two', 'bar'
+ I18n.stubs(:translate).with(key).returns(expected)
+ assert_equal expected, @controller.t(key)
+ end
+
+ def test_localize
+ time, expected = Time.gm(2000), 'Sat, 01 Jan 2000 00:00:00 +0000'
+ I18n.stubs(:localize).with(time).returns(expected)
+ assert_equal expected, @controller.l(time)
+ end
+ end
end
end
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index bbcd289886..7157bccfb3 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -38,6 +38,8 @@ end
ActiveSupport::Dependencies.hook!
+Thread.abort_on_exception = true
+
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
diff --git a/actionpack/test/activerecord/form_helper_activerecord_test.rb b/actionpack/test/activerecord/form_helper_activerecord_test.rb
new file mode 100644
index 0000000000..2e302c65a7
--- /dev/null
+++ b/actionpack/test/activerecord/form_helper_activerecord_test.rb
@@ -0,0 +1,91 @@
+require 'active_record_unit'
+require 'fixtures/project'
+require 'fixtures/developer'
+
+class FormHelperActiveRecordTest < ActionView::TestCase
+ tests ActionView::Helpers::FormHelper
+
+ def form_for(*)
+ @output_buffer = super
+ end
+
+ def setup
+ @developer = Developer.new
+ @developer.id = 123
+ @developer.name = "developer #123"
+
+ @project = Project.new
+ @project.id = 321
+ @project.name = "project #321"
+ @project.save
+
+ @developer.projects << @project
+ @developer.save
+ end
+
+ def teardown
+ Project.delete(321)
+ Developer.delete(123)
+ end
+
+ Routes = ActionDispatch::Routing::RouteSet.new
+ Routes.draw do
+ resources :developers do
+ resources :projects
+ end
+ end
+
+ def _routes
+ Routes
+ end
+
+ include Routes.url_helpers
+
+ def test_nested_fields_for_with_child_index_option_override_on_a_nested_attributes_collection_association
+ form_for(@developer) do |f|
+ concat f.fields_for(:projects, @developer.projects.first, :child_index => 'abc') { |cf|
+ concat cf.text_field(:name)
+ }
+ end
+
+ expected = whole_form('/developers/123', 'edit_developer_123', 'edit_developer', :method => 'patch') do
+ '<input id="developer_projects_attributes_abc_name" name="developer[projects_attributes][abc][name]" type="text" value="project #321" />' +
+ '<input id="developer_projects_attributes_abc_id" name="developer[projects_attributes][abc][id]" type="hidden" value="321" />'
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ protected
+
+ def hidden_fields(method = nil)
+ txt = %{<div style="margin:0;padding:0;display:inline">}
+ txt << %{<input name="utf8" type="hidden" value="&#x2713;" />}
+ if method && !%w(get post).include?(method.to_s)
+ txt << %{<input name="_method" type="hidden" value="#{method}" />}
+ end
+ txt << %{</div>}
+ end
+
+ def form_text(action = "/", id = nil, html_class = nil, remote = nil, multipart = nil, method = nil)
+ txt = %{<form accept-charset="UTF-8" action="#{action}"}
+ txt << %{ enctype="multipart/form-data"} if multipart
+ txt << %{ data-remote="true"} if remote
+ txt << %{ class="#{html_class}"} if html_class
+ txt << %{ id="#{id}"} if id
+ method = method.to_s == "get" ? "get" : "post"
+ txt << %{ method="#{method}">}
+ end
+
+ def whole_form(action = "/", id = nil, html_class = nil, options = nil)
+ contents = block_given? ? yield : ""
+
+ if options.is_a?(Hash)
+ method, remote, multipart = options.values_at(:method, :remote, :multipart)
+ else
+ method = options
+ end
+
+ form_text(action, id, html_class, remote, multipart, method) + hidden_fields(method) + contents + "</form>"
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index e2239c05c7..72b882539c 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -751,13 +751,17 @@ class UrlOptionsIntegrationTest < ActionDispatch::IntegrationTest
assert_equal "http://bar.com/foo", foos_url
end
- test "test can override default url options" do
+ def test_can_override_default_url_options
+ original_host = default_url_options.dup
+
default_url_options[:host] = "foobar.com"
assert_equal "http://foobar.com/foo", foos_url
get "/bar"
assert_response :success
assert_equal "http://foobar.com/foo", foos_url
+ ensure
+ ActionDispatch::Integration::Session.default_url_options = self.default_url_options = original_host
end
test "current request path parameters are recalled" do
diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb
index 94a8d2f180..71bcfd664e 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -1,5 +1,6 @@
require 'abstract_unit'
require 'rbconfig'
+require 'active_support/core_ext/array/extract_options'
# The view_paths array must be set on Base and not LayoutTest so that LayoutTest's inherited
# method has access to the view_paths array when looking for a layout to automatically assign.
@@ -80,7 +81,7 @@ end
class StreamingLayoutController < LayoutTest
def render(*args)
- options = args.extract_options! || {}
+ options = args.extract_options!
super(*args, options.merge(:stream => true))
end
end
diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb
index 41ff2f3809..bac1d02977 100644
--- a/actionpack/test/controller/localized_templates_test.rb
+++ b/actionpack/test/controller/localized_templates_test.rb
@@ -9,14 +9,20 @@ class LocalizedTemplatesTest < ActionController::TestCase
tests LocalizedController
def test_localized_template_is_used
+ old_locale = I18n.locale
I18n.locale = :de
get :hello_world
assert_equal "Gutten Tag", @response.body
+ ensure
+ I18n.locale = old_locale
end
def test_default_locale_template_is_used_when_locale_is_missing
+ old_locale = I18n.locale
I18n.locale = :dk
get :hello_world
assert_equal "Hello World", @response.body
+ ensure
+ I18n.locale = old_locale
end
-end \ No newline at end of file
+end
diff --git a/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb
new file mode 100644
index 0000000000..22e603b881
--- /dev/null
+++ b/actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb
@@ -0,0 +1,50 @@
+require 'abstract_unit'
+require 'action_controller/metal/strong_parameters'
+
+class LogOnUnpermittedParamsTest < ActiveSupport::TestCase
+ def setup
+ ActionController::Parameters.action_on_unpermitted_parameters = :log
+ end
+
+ def teardown
+ ActionController::Parameters.action_on_unpermitted_parameters = false
+ end
+
+ test "logs on unexpected params" do
+ params = ActionController::Parameters.new({
+ book: { pages: 65 },
+ fishing: "Turnips"
+ })
+
+ assert_logged("Unpermitted parameters: fishing") do
+ params.permit(book: [:pages])
+ end
+ end
+
+ test "logs on unexpected nested params" do
+ params = ActionController::Parameters.new({
+ book: { pages: 65, title: "Green Cats and where to find then." }
+ })
+
+ assert_logged("Unpermitted parameters: title") do
+ params.permit(book: [:pages])
+ end
+ end
+
+ private
+
+ def assert_logged(message)
+ old_logger = ActionController::Base.logger
+ log = StringIO.new
+ ActionController::Base.logger = Logger.new(log)
+
+ begin
+ yield
+
+ log.rewind
+ assert_match message, log.read
+ ensure
+ ActionController::Base.logger = old_logger
+ end
+ end
+end
diff --git a/actionpack/test/controller/parameters/nested_parameters_test.rb b/actionpack/test/controller/parameters/nested_parameters_test.rb
index 6df849c4e2..91df527dec 100644
--- a/actionpack/test/controller/parameters/nested_parameters_test.rb
+++ b/actionpack/test/controller/parameters/nested_parameters_test.rb
@@ -2,6 +2,10 @@ require 'abstract_unit'
require 'action_controller/metal/strong_parameters'
class NestedParametersTest < ActiveSupport::TestCase
+ def assert_filtered_out(params, key)
+ assert !params.has_key?(key), "key #{key.inspect} has not been filtered out"
+ end
+
test "permitted nested parameters" do
params = ActionController::Parameters.new({
book: {
@@ -11,6 +15,8 @@ class NestedParametersTest < ActiveSupport::TestCase
born: "1564-04-26"
}, {
name: "Christopher Marlowe"
+ }, {
+ name: %w(malicious injected names)
}],
details: {
pages: 200,
@@ -30,10 +36,12 @@ class NestedParametersTest < ActiveSupport::TestCase
assert_equal "William Shakespeare", permitted[:book][:authors][0][:name]
assert_equal "Christopher Marlowe", permitted[:book][:authors][1][:name]
assert_equal 200, permitted[:book][:details][:pages]
- assert_nil permitted[:book][:id]
- assert_nil permitted[:book][:details][:genre]
- assert_nil permitted[:book][:authors][0][:born]
- assert_nil permitted[:magazine]
+
+ assert_filtered_out permitted, :magazine
+ assert_filtered_out permitted[:book], :id
+ assert_filtered_out permitted[:book][:details], :genre
+ assert_filtered_out permitted[:book][:authors][0], :born
+ assert_filtered_out permitted[:book][:authors][2], :name
end
test "permitted nested parameters with a string or a symbol as a key" do
@@ -63,25 +71,25 @@ class NestedParametersTest < ActiveSupport::TestCase
test "nested arrays with strings" do
params = ActionController::Parameters.new({
- :book => {
- :genres => ["Tragedy"]
+ book: {
+ genres: ["Tragedy"]
}
})
- permitted = params.permit :book => :genres
+ permitted = params.permit book: {genres: []}
assert_equal ["Tragedy"], permitted[:book][:genres]
end
test "permit may specify symbols or strings" do
params = ActionController::Parameters.new({
- :book => {
- :title => "Romeo and Juliet",
- :author => "William Shakespeare"
+ book: {
+ title: "Romeo and Juliet",
+ author: "William Shakespeare"
},
- :magazine => "Shakespeare Today"
+ magazine: "Shakespeare Today"
})
- permitted = params.permit({:book => ["title", :author]}, "magazine")
+ permitted = params.permit({book: ["title", :author]}, "magazine")
assert_equal "Romeo and Juliet", permitted[:book][:title]
assert_equal "William Shakespeare", permitted[:book][:author]
assert_equal "Shakespeare Today", permitted[:magazine]
@@ -127,16 +135,38 @@ class NestedParametersTest < ActiveSupport::TestCase
book: {
authors_attributes: {
:'0' => { name: 'William Shakespeare', age_of_death: '52' },
- :'-1' => { name: 'Unattributed Assistant' }
+ :'1' => { name: 'Unattributed Assistant' },
+ :'2' => { name: %w(injected names)}
}
}
})
permitted = params.permit book: { authors_attributes: [ :name ] }
assert_not_nil permitted[:book][:authors_attributes]['0']
- assert_not_nil permitted[:book][:authors_attributes]['-1']
- assert_nil permitted[:book][:authors_attributes]['0'][:age_of_death]
+ assert_not_nil permitted[:book][:authors_attributes]['1']
+ assert_empty permitted[:book][:authors_attributes]['2']
assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['0'][:name]
- assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['-1'][:name]
+ assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['1'][:name]
+
+ assert_filtered_out permitted[:book][:authors_attributes]['0'], :age_of_death
+ end
+
+ test "fields_for-style nested params with negative numbers" do
+ params = ActionController::Parameters.new({
+ book: {
+ authors_attributes: {
+ :'-1' => { name: 'William Shakespeare', age_of_death: '52' },
+ :'-2' => { name: 'Unattributed Assistant' }
+ }
+ }
+ })
+ permitted = params.permit book: { authors_attributes: [:name] }
+
+ assert_not_nil permitted[:book][:authors_attributes]['-1']
+ assert_not_nil permitted[:book][:authors_attributes]['-2']
+ assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['-1'][:name]
+ assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['-2'][:name]
+
+ assert_filtered_out permitted[:book][:authors_attributes]['-1'], :age_of_death
end
end
diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb
index 7cc71fe6dc..aadb142660 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -1,11 +1,133 @@
require 'abstract_unit'
+require 'action_dispatch/http/upload'
require 'action_controller/metal/strong_parameters'
class ParametersPermitTest < ActiveSupport::TestCase
+ def assert_filtered_out(params, key)
+ assert !params.has_key?(key), "key #{key.inspect} has not been filtered out"
+ end
+
setup do
@params = ActionController::Parameters.new({ person: {
age: "32", name: { first: "David", last: "Heinemeier Hansson" }
}})
+
+ @struct_fields = []
+ %w(0 1 12).each do |number|
+ ['', 'i', 'f'].each do |suffix|
+ @struct_fields << "sf(#{number}#{suffix})"
+ end
+ end
+ end
+
+ test 'if nothing is permitted, the hash becomes empty' do
+ params = ActionController::Parameters.new(id: '1234')
+ permitted = params.permit
+ assert permitted.permitted?
+ assert permitted.empty?
+ end
+
+ test 'key: permitted scalar values' do
+ values = ['a', :a, nil]
+ values += [0, 1.0, 2**128, BigDecimal.new(1)]
+ values += [true, false]
+ values += [Date.today, Time.now, DateTime.now]
+ values += [STDOUT, StringIO.new, ActionDispatch::Http::UploadedFile.new(tempfile: __FILE__)]
+
+ values.each do |value|
+ params = ActionController::Parameters.new(id: value)
+ permitted = params.permit(:id)
+ assert_equal value, permitted[:id]
+
+ @struct_fields.each do |sf|
+ params = ActionController::Parameters.new(sf => value)
+ permitted = params.permit(:sf)
+ assert_equal value, permitted[sf]
+ end
+ end
+ end
+
+ test 'key: unknown keys are filtered out' do
+ params = ActionController::Parameters.new(id: '1234', injected: 'injected')
+ permitted = params.permit(:id)
+ assert_equal '1234', permitted[:id]
+ assert_filtered_out permitted, :injected
+ end
+
+ test 'key: arrays are filtered out' do
+ [[], [1], ['1']].each do |array|
+ params = ActionController::Parameters.new(id: array)
+ permitted = params.permit(:id)
+ assert_filtered_out permitted, :id
+
+ @struct_fields.each do |sf|
+ params = ActionController::Parameters.new(sf => array)
+ permitted = params.permit(:sf)
+ assert_filtered_out permitted, sf
+ end
+ end
+ end
+
+ test 'key: hashes are filtered out' do
+ [{}, {foo: 1}, {foo: 'bar'}].each do |hash|
+ params = ActionController::Parameters.new(id: hash)
+ permitted = params.permit(:id)
+ assert_filtered_out permitted, :id
+
+ @struct_fields.each do |sf|
+ params = ActionController::Parameters.new(sf => hash)
+ permitted = params.permit(:sf)
+ assert_filtered_out permitted, sf
+ end
+ end
+ end
+
+ test 'key: non-permitted scalar values are filtered out' do
+ params = ActionController::Parameters.new(id: Object.new)
+ permitted = params.permit(:id)
+ assert_filtered_out permitted, :id
+
+ @struct_fields.each do |sf|
+ params = ActionController::Parameters.new(sf => Object.new)
+ permitted = params.permit(:sf)
+ assert_filtered_out permitted, sf
+ end
+ end
+
+ test 'key: it is not assigned if not present in params' do
+ params = ActionController::Parameters.new(name: 'Joe')
+ permitted = params.permit(:id)
+ assert !permitted.has_key?(:id)
+ end
+
+ test 'key to empty array: empty arrays pass' do
+ params = ActionController::Parameters.new(id: [])
+ permitted = params.permit(id: [])
+ assert_equal [], permitted[:id]
+ end
+
+ test 'key to empty array: arrays of permitted scalars pass' do
+ [['foo'], [1], ['foo', 'bar'], [1, 2, 3]].each do |array|
+ params = ActionController::Parameters.new(id: array)
+ permitted = params.permit(id: [])
+ assert_equal array, permitted[:id]
+ end
+ end
+
+ test 'key to empty array: permitted scalar values do not pass' do
+ ['foo', 1].each do |permitted_scalar|
+ params = ActionController::Parameters.new(id: permitted_scalar)
+ permitted = params.permit(id: [])
+ assert_filtered_out permitted, :id
+ end
+ end
+
+ test 'key to empty array: arrays of non-permitted scalar do not pass' do
+ [[Object.new], [[]], [[1]], [{}], [{id: '1'}]].each do |non_permitted_scalar|
+ params = ActionController::Parameters.new(id: non_permitted_scalar)
+ permitted = params.permit(id: [])
+ assert_filtered_out permitted, :id
+ end
end
test "fetch raises ParameterMissing exception" do
@@ -73,10 +195,6 @@ class ParametersPermitTest < ActiveSupport::TestCase
assert_equal "Jonas", @params[:person][:family][:brother]
end
- test "permitting parameters that are not there should not include the keys" do
- assert !@params.permit(:person, :funky).has_key?(:funky)
- end
-
test "permit state is kept on a dup" do
@params.permit!
assert_equal @params.permitted?, @params.dup.permitted?
diff --git a/actionpack/test/controller/parameters/raise_on_unpermitted_params_test.rb b/actionpack/test/controller/parameters/raise_on_unpermitted_params_test.rb
new file mode 100644
index 0000000000..f9cc9f96f1
--- /dev/null
+++ b/actionpack/test/controller/parameters/raise_on_unpermitted_params_test.rb
@@ -0,0 +1,33 @@
+require 'abstract_unit'
+require 'action_controller/metal/strong_parameters'
+
+class RaiseOnUnpermittedParamsTest < ActiveSupport::TestCase
+ def setup
+ ActionController::Parameters.action_on_unpermitted_parameters = :raise
+ end
+
+ def teardown
+ ActionController::Parameters.action_on_unpermitted_parameters = false
+ end
+
+ test "raises on unexpected params" do
+ params = ActionController::Parameters.new({
+ book: { pages: 65 },
+ fishing: "Turnips"
+ })
+
+ assert_raises(ActionController::UnpermittedParameters) do
+ params.permit(book: [:pages])
+ end
+ end
+
+ test "raises on unexpected nested params" do
+ params = ActionController::Parameters.new({
+ book: { pages: 65, title: "Green Cats and where to find then." }
+ })
+
+ assert_raises(ActionController::UnpermittedParameters) do
+ params.permit(book: [:pages])
+ end
+ end
+end
diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb
new file mode 100644
index 0000000000..ff5d7fd3bd
--- /dev/null
+++ b/actionpack/test/controller/record_identifier_test.rb
@@ -0,0 +1,34 @@
+require 'abstract_unit'
+require 'controller/fake_models'
+
+class ControllerRecordIdentifierTest < ActiveSupport::TestCase
+ include ActionController::RecordIdentifier
+
+ def setup
+ @record = Comment.new
+ end
+
+ def test_dom_id_deprecation
+ assert_deprecated(/dom_id method will no longer be included by default in controllers/) do
+ dom_id(@record)
+ end
+ end
+
+ def test_dom_class_deprecation
+ assert_deprecated(/dom_class method will no longer be included by default in controllers/) do
+ dom_class(@record)
+ end
+ end
+
+ def test_dom_id_from_module_deprecation
+ assert_deprecated(/Calling ActionController::RecordIdentifier.dom_id is deprecated/) do
+ ActionController::RecordIdentifier.dom_id(@record)
+ end
+ end
+
+ def test_dom_class_from_module_deprecation
+ assert_deprecated(/Calling ActionController::RecordIdentifier.dom_class is deprecated/) do
+ ActionController::RecordIdentifier.dom_class(@record)
+ end
+ end
+end
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 523a8d0572..c272e785c2 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -66,6 +66,19 @@ class RequestForgeryProtectionControllerUsingException < ActionController::Base
protect_from_forgery :only => %w(index meta), :with => :exception
end
+class RequestForgeryProtectionControllerUsingNullSession < ActionController::Base
+ protect_from_forgery :with => :null_session
+
+ def signed
+ cookies.signed[:foo] = 'bar'
+ render :nothing => true
+ end
+
+ def encrypted
+ cookies.encrypted[:foo] = 'bar'
+ render :nothing => true
+ end
+end
class FreeCookieController < RequestForgeryProtectionControllerUsingResetSession
self.allow_forgery_protection = false
@@ -170,6 +183,10 @@ module RequestForgeryProtectionTests
assert_not_blocked { get :index }
end
+ def test_should_allow_head
+ assert_not_blocked { head :index }
+ end
+
def test_should_allow_post_without_token_on_unsafe_action
assert_not_blocked { post :unsafe }
end
@@ -283,6 +300,28 @@ class RequestForgeryProtectionControllerUsingResetSessionTest < ActionController
end
end
+class NullSessionDummyKeyGenerator
+ def generate_key(secret)
+ '03312270731a2ed0d11ed091c2338a06'
+ end
+end
+
+class RequestForgeryProtectionControllerUsingNullSessionTest < ActionController::TestCase
+ def setup
+ @request.env[ActionDispatch::Cookies::GENERATOR_KEY] = NullSessionDummyKeyGenerator.new
+ end
+
+ test 'should allow to set signed cookies' do
+ post :signed
+ assert_response :ok
+ end
+
+ test 'should allow to set encrypted cookies' do
+ post :encrypted
+ assert_response :ok
+ end
+end
+
class RequestForgeryProtectionControllerUsingExceptionTest < ActionController::TestCase
include RequestForgeryProtectionTests
def assert_blocked
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index 305659b219..9aea7e860a 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/with_options'
+require 'active_support/core_ext/array/extract_options'
class ResourcesTest < ActionController::TestCase
def test_default_restful_routes
diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb
index 0480295056..19d5652d81 100644
--- a/actionpack/test/controller/webservice_test.rb
+++ b/actionpack/test/controller/webservice_test.rb
@@ -129,19 +129,6 @@ class WebServiceTest < ActionDispatch::IntegrationTest
$stderr = STDERR
end
- def test_register_and_use_yaml
- with_test_route_set do
- with_params_parsers Mime::YAML => Proc.new { |d| YAML.load(d) } do
- post "/", {"entry" => "loaded from yaml"}.to_yaml,
- {'CONTENT_TYPE' => 'application/x-yaml'}
-
- assert_equal 'entry', @controller.response.body
- assert @controller.params.has_key?(:entry)
- assert_equal 'loaded from yaml', @controller.params["entry"]
- end
- end
- end
-
def test_register_and_use_xml_simple
with_test_route_set do
with_params_parsers Mime::XML => Proc.new { |data| Hash.from_xml(data)['request'].with_indifferent_access } do
diff --git a/actionpack/test/dispatch/best_standards_support_test.rb b/actionpack/test/dispatch/best_standards_support_test.rb
deleted file mode 100644
index 551bb9621a..0000000000
--- a/actionpack/test/dispatch/best_standards_support_test.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'abstract_unit'
-
-class BestStandardsSupportTest < ActiveSupport::TestCase
- def test_with_best_standards_support
- _, headers, _ = app(true, {}).call({})
- assert_equal "IE=Edge,chrome=1", headers["X-UA-Compatible"]
- end
-
- def test_with_builtin_best_standards_support
- _, headers, _ = app(:builtin, {}).call({})
- assert_equal "IE=Edge", headers["X-UA-Compatible"]
- end
-
- def test_without_best_standards_support
- _, headers, _ = app(false, {}).call({})
- assert_equal nil, headers["X-UA-Compatible"]
- end
-
- def test_appends_to_app_headers_without_duplication_after_multiple_requests
- app_headers = { "X-UA-Compatible" => "requiresActiveX=true" }
- _, headers, _ = app(true, app_headers).call({})
- _, headers, _ = app(true, app_headers).call({})
-
- expects = "requiresActiveX=true,IE=Edge,chrome=1"
- assert_equal expects, headers["X-UA-Compatible"]
- end
-
- private
-
- def app(type, headers)
- app = proc { [200, headers, "response"] }
- ActionDispatch::BestStandardsSupport.new(app, type)
- end
-
-end
diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb
index ffa91d63c4..66d2cade22 100644
--- a/actionpack/test/dispatch/cookies_test.rb
+++ b/actionpack/test/dispatch/cookies_test.rb
@@ -160,6 +160,36 @@ class CookiesTest < ActionController::TestCase
@request.host = "www.nextangle.com"
end
+ def test_fetch
+ x = Object.new
+ assert_not request.cookie_jar.key?('zzzzzz')
+ assert_equal x, request.cookie_jar.fetch('zzzzzz', x)
+ assert_not request.cookie_jar.key?('zzzzzz')
+ end
+
+ def test_fetch_exists
+ x = Object.new
+ request.cookie_jar['foo'] = 'bar'
+ assert_equal 'bar', request.cookie_jar.fetch('foo', x)
+ end
+
+ def test_fetch_block
+ x = Object.new
+ assert_not request.cookie_jar.key?('zzzzzz')
+ assert_equal x, request.cookie_jar.fetch('zzzzzz') { x }
+ end
+
+ def test_key_is_to_s
+ request.cookie_jar['foo'] = 'bar'
+ assert_equal 'bar', request.cookie_jar.fetch(:foo)
+ end
+
+ def test_fetch_type_error
+ assert_raises(KeyError) do
+ request.cookie_jar.fetch(:omglolwut)
+ end
+ end
+
def test_each
request.cookie_jar['foo'] = :bar
list = []
@@ -211,13 +241,13 @@ class CookiesTest < ActionController::TestCase
def test_setting_cookie_for_fourteen_days
get :authenticate_for_fourteen_days
- assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"
+ assert_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000"
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_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"
+ assert_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000"
assert_equal({"user_name" => "david"}, @response.cookies)
end
@@ -250,7 +280,7 @@ class CookiesTest < ActionController::TestCase
def test_multiple_cookies
get :set_multiple_cookies
assert_equal 2, @response.cookies.size
- assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT\nlogin=XJ-122; path=/"
+ assert_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000\nlogin=XJ-122; path=/"
assert_equal({"login" => "XJ-122", "user_name" => "david"}, @response.cookies)
end
@@ -261,14 +291,14 @@ class CookiesTest < ActionController::TestCase
def test_expiring_cookie
request.cookies[:user_name] = 'Joe'
get :logout
- assert_cookie_header "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
+ assert_cookie_header "user_name=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
assert_equal({"user_name" => nil}, @response.cookies)
end
def test_delete_cookie_with_path
request.cookies[:user_name] = 'Joe'
get :delete_cookie_with_path
- assert_cookie_header "user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"
+ assert_cookie_header "user_name=; path=/beaten; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
end
def test_delete_unexisting_cookie
@@ -330,7 +360,7 @@ class CookiesTest < ActionController::TestCase
def test_delete_and_set_cookie
request.cookies[:user_name] = 'Joe'
get :delete_and_set_cookie
- assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"
+ assert_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000"
assert_equal({"user_name" => "david"}, @response.cookies)
end
@@ -435,7 +465,7 @@ class CookiesTest < ActionController::TestCase
request.cookies[:user_name] = 'Joe'
get :delete_cookie_with_domain
assert_response :success
- assert_cookie_header "user_name=; domain=.nextangle.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
+ assert_cookie_header "user_name=; domain=.nextangle.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
end
def test_cookie_with_all_domain_option_and_tld_length
@@ -462,7 +492,7 @@ class CookiesTest < ActionController::TestCase
request.cookies[:user_name] = 'Joe'
get :delete_cookie_with_domain_and_tld
assert_response :success
- assert_cookie_header "user_name=; domain=.nextangle.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
+ assert_cookie_header "user_name=; domain=.nextangle.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
end
def test_cookie_with_several_preset_domains_using_one_of_these_domains
@@ -491,7 +521,7 @@ class CookiesTest < ActionController::TestCase
request.cookies[:user_name] = 'Joe'
get :delete_cookie_with_domains
assert_response :success
- assert_cookie_header "user_name=; domain=example2.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
+ assert_cookie_header "user_name=; domain=example2.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
end
def test_deletings_cookie_with_several_preset_domains_using_other_domain
@@ -499,7 +529,7 @@ class CookiesTest < ActionController::TestCase
request.cookies[:user_name] = 'Joe'
get :delete_cookie_with_domains
assert_response :success
- assert_cookie_header "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
+ assert_cookie_header "user_name=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
end
def test_cookies_hash_is_indifferent_access
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index cbb086f605..91810864d5 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -590,6 +590,10 @@ class RequestTest < ActiveSupport::TestCase
request.expects(:parameters).at_least_once.returns({})
assert_equal [Mime::HTML], request.formats
+ request = stub_request 'HTTP_ACCEPT' => ''
+ request.expects(:parameters).at_least_once.returns({})
+ assert_equal [Mime::HTML], request.formats
+
request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8',
'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"
request.expects(:parameters).at_least_once.returns({})
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 185a9e9b18..74f5253c11 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -35,7 +35,7 @@ class ResponseTest < ActiveSupport::TestCase
end
def test_response_body_encoding
- body = ["hello".encode('utf-8')]
+ body = ["hello".encode(Encoding::UTF_8)]
response = ActionDispatch::Response.new 200, {}, body
assert_equal Encoding::UTF_8, response.body.encoding
end
@@ -129,7 +129,7 @@ class ResponseTest < ActiveSupport::TestCase
@response.set_cookie("login", :value => "foo&bar", :path => "/", :expires => Time.utc(2005, 10, 10,5))
status, headers, body = @response.to_a
- assert_equal "user_name=david; path=/\nlogin=foo%26bar; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT", headers["Set-Cookie"]
+ assert_equal "user_name=david; path=/\nlogin=foo%26bar; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000", headers["Set-Cookie"]
assert_equal({"login" => "foo&bar", "user_name" => "david"}, @response.cookies)
@response.delete_cookie("login")
@@ -182,7 +182,8 @@ class ResponseTest < ActiveSupport::TestCase
ActionDispatch::Response.default_headers = {
'X-Frame-Options' => 'DENY',
'X-Content-Type-Options' => 'nosniff',
- 'X-XSS-Protection' => '1;'
+ 'X-XSS-Protection' => '1;',
+ 'X-UA-Compatible' => 'chrome=1'
}
resp = ActionDispatch::Response.new.tap { |response|
response.body = 'Hello'
@@ -192,6 +193,7 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal('DENY', resp.headers['X-Frame-Options'])
assert_equal('nosniff', resp.headers['X-Content-Type-Options'])
assert_equal('1;', resp.headers['X-XSS-Protection'])
+ assert_equal('chrome=1', resp.headers['X-UA-Compatible'])
ensure
ActionDispatch::Response.default_headers = nil
end
diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb
index c7dcb5a683..55221f87c4 100644
--- a/actionpack/test/dispatch/routing/inspector_test.rb
+++ b/actionpack/test/dispatch/routing/inspector_test.rb
@@ -21,6 +21,14 @@ module ActionDispatch
inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, options[:filter]).split("\n")
end
+ def test_json_regexp_converter
+ @set.draw do
+ get '/cart', :to => 'cart#show'
+ end
+ route = ActionDispatch::Routing::RouteWrapper.new(@set.routes.first)
+ assert_equal "^\\/cart(?:\\.([^\\/.?]+))?$", route.json_regexp
+ end
+
def test_displaying_routes_for_engines
engine = Class.new(Rails::Engine) do
def self.inspect
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index da7474e73c..143733254b 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -1031,6 +1031,18 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal 'users/home#index', @response.body
end
+ def test_namespace_containing_numbers
+ draw do
+ namespace :v2 do
+ resources :subscriptions
+ end
+ end
+
+ get '/v2/subscriptions'
+ assert_equal 'v2/subscriptions#index', @response.body
+ assert_equal '/v2/subscriptions', v2_subscriptions_path
+ end
+
def test_articles_with_id
draw do
controller :articles do
@@ -2678,6 +2690,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '0c0c0b68-d24b-11e1-a861-001ff3fffe6f', @request.params[:download]
end
+ def test_action_from_path_is_not_frozen
+ draw do
+ get 'search' => 'search'
+ end
+
+ get '/search'
+ assert !@request.params[:action].frozen?
+ end
+
private
def draw(&block)
@@ -2824,21 +2845,52 @@ class TestNamespaceWithControllerOption < ActionDispatch::IntegrationTest
end
end
- DefaultScopeRoutes = ActionDispatch::Routing::RouteSet.new
- DefaultScopeRoutes.draw do
- namespace :admin do
- resources :storage_files, :controller => "StorageFiles"
- end
+ def draw(&block)
+ @app = ActionDispatch::Routing::RouteSet.new
+ @app.draw(&block)
end
- def app
- DefaultScopeRoutes
- end
+ def test_valid_controller_options_inside_namespace
+ draw do
+ namespace :admin do
+ resources :storage_files, :controller => "storage_files"
+ end
+ end
- def test_controller_options
get '/admin/storage_files'
assert_equal "admin/storage_files#index", @response.body
end
+
+ def test_resources_with_valid_namespaced_controller_option
+ draw do
+ resources :storage_files, :controller => 'admin/storage_files'
+ end
+
+ get 'storage_files'
+ assert_equal "admin/storage_files#index", @response.body
+ end
+
+ def test_warn_with_ruby_constant_syntax_controller_option
+ e = assert_raise(ArgumentError) do
+ draw do
+ namespace :admin do
+ resources :storage_files, :controller => "StorageFiles"
+ end
+ end
+ end
+
+ assert_match "'admin/StorageFiles' is not a supported controller name", e.message
+ end
+
+ def test_warn_with_ruby_constant_syntax_namespaced_controller_option
+ e = assert_raise(ArgumentError) do
+ draw do
+ resources :storage_files, :controller => 'Admin::StorageFiles'
+ end
+ end
+
+ assert_match "'Admin::StorageFiles' is not a supported controller name", e.message
+ end
end
class TestDefaultScope < ActionDispatch::IntegrationTest
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index 1677dee524..d8bf22dec8 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -276,7 +276,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
# First request accesses the session
time = Time.local(2008, 4, 24)
Time.stubs(:now).returns(time)
- expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d-%b-%Y %H:%M:%S GMT")
+ expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000")
cookies[SessionKey] = SignedBar
@@ -290,7 +290,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
# Second request does not access the session
time = Time.local(2008, 4, 25)
Time.stubs(:now).returns(time)
- expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d-%b-%Y %H:%M:%S GMT")
+ expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000")
get '/no_session_access'
assert_response :success
diff --git a/actionpack/test/dispatch/session/test_session_test.rb b/actionpack/test/dispatch/session/test_session_test.rb
index 904398f563..d30461a623 100644
--- a/actionpack/test/dispatch/session/test_session_test.rb
+++ b/actionpack/test/dispatch/session/test_session_test.rb
@@ -2,8 +2,8 @@ require 'abstract_unit'
require 'stringio'
class ActionController::TestSessionTest < ActiveSupport::TestCase
- def test_ctor_allows_setting
- session = ActionController::TestSession.new({:one => 'one', :two => 'two'})
+ def test_initialize_with_values
+ session = ActionController::TestSession.new(one: 'one', two: 'two')
assert_equal('one', session[:one])
assert_equal('two', session[:two])
end
@@ -23,15 +23,21 @@ class ActionController::TestSessionTest < ActiveSupport::TestCase
end
def test_calling_update_with_params_passes_to_attributes
- session = ActionController::TestSession.new()
+ session = ActionController::TestSession.new
session.update('key' => 'value')
assert_equal('value', session[:key])
end
- def test_clear_emptys_session
- session = ActionController::TestSession.new({:one => 'one', :two => 'two'})
+ def test_clear_empties_session
+ session = ActionController::TestSession.new(one: 'one', two: 'two')
session.clear
assert_nil(session[:one])
assert_nil(session[:two])
end
+
+ def test_keys_and_values
+ session = ActionController::TestSession.new(one: '1', two: '2')
+ assert_equal %w(one two), session.keys
+ assert_equal %w(1 2), session.values
+ end
end
diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb
index 6047631ba3..3db862c810 100644
--- a/actionpack/test/dispatch/test_request_test.rb
+++ b/actionpack/test/dispatch/test_request_test.rb
@@ -18,7 +18,7 @@ class TestRequestTest < ActiveSupport::TestCase
assert_equal "0.0.0.0", env.delete("REMOTE_ADDR")
assert_equal "Rails Testing", env.delete("HTTP_USER_AGENT")
- assert_equal [1, 1], env.delete("rack.version")
+ assert_equal [1, 2], env.delete("rack.version")
assert_equal "", env.delete("rack.input").string
assert_kind_of StringIO, env.delete("rack.errors")
assert_equal true, env.delete("rack.multithread")
@@ -40,10 +40,10 @@ class TestRequestTest < ActiveSupport::TestCase
req.cookie_jar["login"] = "XJ-122"
assert_cookies({"user_name" => "david", "login" => "XJ-122"}, req.cookie_jar)
- assert_nothing_raised do
+ assert_nothing_raised do
req.cookie_jar["login"] = nil
assert_cookies({"user_name" => "david", "login" => nil}, req.cookie_jar)
- end
+ end
req.cookie_jar.delete(:login)
assert_cookies({"user_name" => "david"}, req.cookie_jar)
diff --git a/actionpack/test/fixtures/developer.rb b/actionpack/test/fixtures/developer.rb
index dd14548fac..4941463015 100644
--- a/actionpack/test/fixtures/developer.rb
+++ b/actionpack/test/fixtures/developer.rb
@@ -2,6 +2,7 @@ class Developer < ActiveRecord::Base
has_and_belongs_to_many :projects
has_many :replies
has_many :topics, :through => :replies
+ accepts_nested_attributes_for :projects
end
class DeVeLoPeR < ActiveRecord::Base
diff --git a/actionpack/test/fixtures/test/_directory/_partial_with_locales.html.erb b/actionpack/test/fixtures/test/_directory/_partial_with_locales.html.erb
new file mode 100644
index 0000000000..1cc8d41475
--- /dev/null
+++ b/actionpack/test/fixtures/test/_directory/_partial_with_locales.html.erb
@@ -0,0 +1 @@
+Hello <%= name %>
diff --git a/actionpack/test/fixtures/test/render_partial_inside_directory.html.erb b/actionpack/test/fixtures/test/render_partial_inside_directory.html.erb
new file mode 100644
index 0000000000..1461b95186
--- /dev/null
+++ b/actionpack/test/fixtures/test/render_partial_inside_directory.html.erb
@@ -0,0 +1 @@
+<%= render partial: 'test/_directory/partial_with_locales', locals: {'name' => 'Jane'} %>
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index 82c9d383ac..11614a45dc 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -443,7 +443,8 @@ class AssetTagHelperTest < ActionView::TestCase
[nil, '/', '/foo/bar/', 'foo/bar/'].each do |prefix|
assert_equal 'Rails', image_alt("#{prefix}rails.png")
assert_equal 'Rails', image_alt("#{prefix}rails-9c0a079bdd7701d7e729bd956823d153.png")
- assert_equal 'Avatar-0000', image_alt("#{prefix}avatar-0000.png")
+ assert_equal 'Long file name with hyphens', image_alt("#{prefix}long-file-name-with-hyphens.png")
+ assert_equal 'Long file name with underscores', image_alt("#{prefix}long_file_name_with_underscores.png")
end
end
diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb
index 495a9d3f9d..21fca35185 100644
--- a/actionpack/test/template/date_helper_i18n_test.rb
+++ b/actionpack/test/template/date_helper_i18n_test.rb
@@ -117,7 +117,7 @@ class DateHelperSelectTagsI18nTests < ActiveSupport::TestCase
I18n.expects(:translate).with(('datetime.prompts.' + key.to_s).to_sym, :locale => 'en').returns prompt
end
- I18n.expects(:translate).with(:'date.order', :locale => 'en', :default => []).returns [:year, :month, :day]
+ I18n.expects(:translate).with(:'date.order', :locale => 'en', :default => []).returns %w(year month day)
datetime_select('post', 'updated_at', :locale => 'en', :include_seconds => true, :prompt => true)
end
@@ -129,15 +129,20 @@ class DateHelperSelectTagsI18nTests < ActiveSupport::TestCase
end
def test_date_or_time_select_given_no_order_options_translates_order
- I18n.expects(:translate).with(:'date.order', :locale => 'en', :default => []).returns [:year, :month, :day]
+ I18n.expects(:translate).with(:'date.order', :locale => 'en', :default => []).returns %w(year month day)
datetime_select('post', 'updated_at', :locale => 'en')
end
def test_date_or_time_select_given_invalid_order
- I18n.expects(:translate).with(:'date.order', :locale => 'en', :default => []).returns [:invalid, :month, :day]
+ I18n.expects(:translate).with(:'date.order', :locale => 'en', :default => []).returns %w(invalid month day)
assert_raise StandardError do
datetime_select('post', 'updated_at', :locale => 'en')
end
end
+
+ def test_date_or_time_select_given_symbol_keys
+ I18n.expects(:translate).with(:'date.order', :locale => 'en', :default => []).returns [:year, :month, :day]
+ datetime_select('post', 'updated_at', :locale => 'en')
+ end
end
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index d622392caf..f11adefad8 100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -1529,7 +1529,24 @@ class DateHelperTest < ActionView::TestCase
expected << "</select>\n"
assert_dom_equal expected, date_select("post", "written_on", :selected => Date.new(2004, 07, 10))
+ end
+
+ def test_date_select_with_selected_nil
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ expected = '<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="1"/>' + "\n"
+
+ expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+ expected << %{<option value=""></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">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=""></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">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", include_blank: true, discard_year: true, selected: nil)
end
def test_date_select_without_day
@@ -2005,7 +2022,26 @@ class DateHelperTest < ActionView::TestCase
0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 20}>#{sprintf("%02d", i)}</option>\n) }
expected << "</select>\n"
- assert_dom_equal expected, time_select("post", "written_on", :selected => Time.local(2004, 6, 15, 12, 20, 30))
+ assert_dom_equal expected, time_select("post", "written_on", selected: Time.local(2004, 6, 15, 12, 20, 30))
+ end
+
+ def test_time_select_with_selected_nil
+ @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="1" />\n}
+ expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="1" />\n}
+ expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="1" />\n}
+
+ expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}">#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+ expected << " : "
+ expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n)
+ 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}">#{sprintf("%02d", i)}</option>\n) }
+ expected << "</select>\n"
+
+ assert_dom_equal expected, time_select("post", "written_on", discard_year: true, discard_month: true, discard_day: true, selected: nil)
end
def test_time_select_without_date_hidden_fields
@@ -2234,6 +2270,33 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, datetime_select("post", "updated_at", :selected => Time.local(2004, 3, 10, 12, 30))
end
+ def test_datetime_select_with_selected_nil
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+
+ expected = '<input id="post_updated_at_1i" name="post[updated_at(1i)]" type="hidden" value="1"/>' + "\n"
+
+ expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\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">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="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">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="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}
+ expected << "</select>\n"
+ expected << " : "
+ expected << %{<select id="post_updated_at_5i" name="post[updated_at(5i)]">\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">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, datetime_select("post", "updated_at", discard_year: true, selected: nil)
+ end
+
def test_datetime_select_defaults_to_time_zone_now_when_config_time_zone_is_set
# The love zone is UTC+0
mytz = Class.new(ActiveSupport::TimeZone) {
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index f9890a2eef..268bab6ad2 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -15,26 +15,26 @@ class FormHelperTest < ActionView::TestCase
# Create "label" locale for testing I18n label helpers
I18n.backend.store_translations 'label', {
- :activemodel => {
- :attributes => {
- :post => {
- :cost => "Total cost"
+ activemodel: {
+ attributes: {
+ post: {
+ cost: "Total cost"
}
}
},
- :helpers => {
- :label => {
- :post => {
- :body => "Write entire text here",
- :color => {
- :red => "Rojo"
+ helpers: {
+ label: {
+ post: {
+ body: "Write entire text here",
+ color: {
+ red: "Rojo"
},
- :comments => {
- :body => "Write body here"
+ comments: {
+ body: "Write body here"
}
},
- :tag => {
- :value => "Tag"
+ tag: {
+ value: "Tag"
}
}
}
@@ -42,13 +42,13 @@ class FormHelperTest < ActionView::TestCase
# Create "submit" locale for testing I18n submit helpers
I18n.backend.store_translations 'submit', {
- :helpers => {
- :submit => {
- :create => 'Create %{model}',
- :update => 'Confirm %{model} changes',
- :submit => 'Save changes',
- :another_post => {
- :update => 'Update your %{model}'
+ helpers: {
+ submit: {
+ create: 'Create %{model}',
+ update: 'Confirm %{model} changes',
+ submit: 'Save changes',
+ another_post: {
+ update: 'Update your %{model}'
}
}
}
@@ -57,11 +57,11 @@ class FormHelperTest < ActionView::TestCase
@post = Post.new
@comment = Comment.new
def @post.errors()
- Class.new{
+ Class.new {
def [](field); field == "author_name" ? ["can't be empty"] : [] end
def empty?() false end
def count() 1 end
- def full_messages() [ "Author name can't be empty" ] end
+ def full_messages() ["Author name can't be empty"] end
}.new
end
def @post.to_key; [123]; end
@@ -96,8 +96,8 @@ class FormHelperTest < ActionView::TestCase
end
end
- get "/foo", :to => "controller#action"
- root :to => "main#index"
+ get "/foo", to: "controller#action"
+ root to: "main#index"
end
def _routes
@@ -108,9 +108,11 @@ class FormHelperTest < ActionView::TestCase
def url_for(object)
@url_for_options = object
+
if object.is_a?(Hash) && object[:use_route].blank? && object[:controller].blank?
- object.merge!(:controller => "main", :action => "index")
+ object.merge!(controller: "main", action: "index")
end
+
super
end
@@ -124,10 +126,13 @@ class FormHelperTest < ActionView::TestCase
def test_label
assert_dom_equal('<label for="post_title">Title</label>', label("post", "title"))
- assert_dom_equal('<label for="post_title">The title goes here</label>', label("post", "title", "The title goes here"))
+ assert_dom_equal(
+ '<label for="post_title">The title goes here</label>',
+ label("post", "title", "The title goes here")
+ )
assert_dom_equal(
'<label class="title_label" for="post_title">Title</label>',
- label("post", "title", nil, :class => 'title_label')
+ label("post", "title", nil, class: 'title_label')
)
assert_dom_equal('<label for="post_secret">Secret?</label>', label("post", "secret?"))
end
@@ -160,28 +165,31 @@ class FormHelperTest < ActionView::TestCase
def test_label_with_locales_and_options
old_locale, I18n.locale = I18n.locale, :label
- assert_dom_equal('<label for="post_body" class="post_body">Write entire text here</label>', label(:post, :body, :class => 'post_body'))
+ assert_dom_equal(
+ '<label for="post_body" class="post_body">Write entire text here</label>',
+ label(:post, :body, class: "post_body")
+ )
ensure
I18n.locale = old_locale
end
def test_label_with_locales_and_value
old_locale, I18n.locale = I18n.locale, :label
- assert_dom_equal('<label for="post_color_red">Rojo</label>', label(:post, :color, :value => "red"))
+ assert_dom_equal('<label for="post_color_red">Rojo</label>', label(:post, :color, value: "red"))
ensure
I18n.locale = old_locale
end
def test_label_with_locales_and_nested_attributes
old_locale, I18n.locale = I18n.locale, :label
- form_for(@post, :html => { :id => 'create-post' }) do |f|
+ form_for(@post, html: { id: 'create-post' }) do |f|
f.fields_for(:comments) do |cf|
concat cf.label(:body)
end
end
- expected = whole_form("/posts/123", "create-post" , "edit_post", :method => 'patch') do
- "<label for=\"post_comments_attributes_0_body\">Write body here</label>"
+ expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do
+ '<label for="post_comments_attributes_0_body">Write body here</label>'
end
assert_dom_equal expected, output_buffer
@@ -191,14 +199,14 @@ class FormHelperTest < ActionView::TestCase
def test_label_with_locales_fallback_and_nested_attributes
old_locale, I18n.locale = I18n.locale, :label
- form_for(@post, :html => { :id => 'create-post' }) do |f|
+ form_for(@post, html: { id: 'create-post' }) do |f|
f.fields_for(:tags) do |cf|
concat cf.label(:value)
end
end
- expected = whole_form("/posts/123", "create-post" , "edit_post", :method => 'patch') do
- "<label for=\"post_tags_attributes_0_value\">Tag</label>"
+ expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do
+ '<label for="post_tags_attributes_0_value">Tag</label>'
end
assert_dom_equal expected, output_buffer
@@ -207,7 +215,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_label_with_for_attribute_as_symbol
- assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, :for => "my_for"))
+ assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, for: "my_for"))
end
def test_label_with_for_attribute_as_string
@@ -215,61 +223,93 @@ class FormHelperTest < ActionView::TestCase
end
def test_label_does_not_generate_for_attribute_when_given_nil
- assert_dom_equal('<label>Title</label>', label(:post, :title, :for => nil))
+ assert_dom_equal('<label>Title</label>', label(:post, :title, for: nil))
end
def test_label_with_id_attribute_as_symbol
- assert_dom_equal('<label for="post_title" id="my_id">Title</label>', label(:post, :title, nil, :id => "my_id"))
+ assert_dom_equal(
+ '<label for="post_title" id="my_id">Title</label>',
+ label(:post, :title, nil, id: "my_id")
+ )
end
def test_label_with_id_attribute_as_string
- assert_dom_equal('<label for="post_title" id="my_id">Title</label>', label(:post, :title, nil, "id" => "my_id"))
+ assert_dom_equal(
+ '<label for="post_title" id="my_id">Title</label>',
+ label(:post, :title, nil, "id" => "my_id")
+ )
end
def test_label_with_for_and_id_attributes_as_symbol
- assert_dom_equal('<label for="my_for" id="my_id">Title</label>', label(:post, :title, nil, :for => "my_for", :id => "my_id"))
+ assert_dom_equal(
+ '<label for="my_for" id="my_id">Title</label>',
+ label(:post, :title, nil, for: "my_for", id: "my_id")
+ )
end
def test_label_with_for_and_id_attributes_as_string
- assert_dom_equal('<label for="my_for" id="my_id">Title</label>', label(:post, :title, nil, "for" => "my_for", "id" => "my_id"))
+ assert_dom_equal(
+ '<label for="my_for" id="my_id">Title</label>',
+ label(:post, :title, nil, "for" => "my_for", "id" => "my_id")
+ )
end
def test_label_for_radio_buttons_with_value
- assert_dom_equal('<label for="post_title_great_title">The title goes here</label>', label("post", "title", "The title goes here", :value => "great_title"))
- assert_dom_equal('<label for="post_title_great_title">The title goes here</label>', label("post", "title", "The title goes here", :value => "great title"))
+ assert_dom_equal(
+ '<label for="post_title_great_title">The title goes here</label>',
+ label("post", "title", "The title goes here", value: "great_title")
+ )
+ assert_dom_equal(
+ '<label for="post_title_great_title">The title goes here</label>',
+ label("post", "title", "The title goes here", value: "great title")
+ )
end
def test_label_with_block
- assert_dom_equal('<label for="post_title">The title, please:</label>', label(:post, :title) { "The title, please:" })
+ assert_dom_equal(
+ '<label for="post_title">The title, please:</label>',
+ label(:post, :title) { "The title, please:" }
+ )
end
def test_label_with_block_and_options
- assert_dom_equal('<label for="my_for">The title, please:</label>', label(:post, :title, "for" => "my_for") { "The title, please:" })
+ assert_dom_equal(
+ '<label for="my_for">The title, please:</label>',
+ label(:post, :title, "for" => "my_for") { "The title, please:" }
+ )
end
def test_label_with_block_in_erb
- assert_equal "<label for=\"post_message\">\n Message\n <input id=\"post_message\" name=\"post[message]\" type=\"text\" />\n</label>", view.render("test/label_with_block")
+ assert_equal(
+ %{<label for="post_message">\n Message\n <input id="post_message" name="post[message]" type="text" />\n</label>},
+ view.render("test/label_with_block")
+ )
end
def test_text_field
assert_dom_equal(
- '<input id="post_title" name="post[title]" type="text" value="Hello World" />', text_field("post", "title")
+ '<input id="post_title" name="post[title]" type="text" value="Hello World" />',
+ text_field("post", "title")
)
assert_dom_equal(
- '<input id="post_title" name="post[title]" type="password" />', password_field("post", "title")
+ '<input id="post_title" name="post[title]" type="password" />',
+ password_field("post", "title")
)
assert_dom_equal(
- '<input id="post_title" name="post[title]" type="password" value="Hello World" />', password_field("post", "title", :value => @post.title)
+ '<input id="post_title" name="post[title]" type="password" value="Hello World" />',
+ password_field("post", "title", value: @post.title)
)
assert_dom_equal(
- '<input id="person_name" name="person[name]" type="password" />', password_field("person", "name")
+ '<input id="person_name" name="person[name]" type="password" />',
+ password_field("person", "name")
)
end
def test_text_field_with_escapes
@post.title = "<b>Hello World</b>"
assert_dom_equal(
- '<input id="post_title" name="post[title]" type="text" value="&lt;b&gt;Hello World&lt;/b&gt;" />', text_field("post", "title")
+ '<input id="post_title" name="post[title]" type="text" value="&lt;b&gt;Hello World&lt;/b&gt;" />',
+ text_field("post", "title")
)
end
@@ -284,29 +324,29 @@ class FormHelperTest < ActionView::TestCase
def test_text_field_with_options
expected = '<input id="post_title" name="post[title]" size="35" type="text" value="Hello World" />'
assert_dom_equal expected, text_field("post", "title", "size" => 35)
- assert_dom_equal expected, text_field("post", "title", :size => 35)
+ assert_dom_equal expected, text_field("post", "title", size: 35)
end
def test_text_field_assuming_size
expected = '<input id="post_title" maxlength="35" name="post[title]" size="35" type="text" value="Hello World" />'
assert_dom_equal expected, text_field("post", "title", "maxlength" => 35)
- assert_dom_equal expected, text_field("post", "title", :maxlength => 35)
+ assert_dom_equal expected, text_field("post", "title", maxlength: 35)
end
def test_text_field_removing_size
expected = '<input id="post_title" maxlength="35" name="post[title]" type="text" value="Hello World" />'
assert_dom_equal expected, text_field("post", "title", "maxlength" => 35, "size" => nil)
- assert_dom_equal expected, text_field("post", "title", :maxlength => 35, :size => nil)
+ assert_dom_equal expected, text_field("post", "title", maxlength: 35, size: nil)
end
def test_text_field_with_nil_value
expected = '<input id="post_title" name="post[title]" type="text" />'
- assert_dom_equal expected, text_field("post", "title", :value => nil)
+ assert_dom_equal expected, text_field("post", "title", value: nil)
end
def test_text_field_with_nil_name
expected = '<input id="post_title" type="text" value="Hello World" />'
- assert_dom_equal expected, text_field("post", "title", :name => nil)
+ assert_dom_equal expected, text_field("post", "title", name: nil)
end
def test_text_field_doesnt_change_param_values
@@ -322,31 +362,41 @@ class FormHelperTest < ActionView::TestCase
end
def test_hidden_field
- assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="Hello World" />',
+ assert_dom_equal(
+ '<input id="post_title" name="post[title]" type="hidden" value="Hello World" />',
hidden_field("post", "title")
- assert_dom_equal '<input id="post_secret" name="post[secret]" type="hidden" value="1" />',
- hidden_field("post", "secret?")
+ )
+ assert_dom_equal(
+ '<input id="post_secret" name="post[secret]" type="hidden" value="1" />',
+ hidden_field("post", "secret?")
+ )
end
def test_hidden_field_with_escapes
@post.title = "<b>Hello World</b>"
- assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="&lt;b&gt;Hello World&lt;/b&gt;" />',
+ assert_dom_equal(
+ '<input id="post_title" name="post[title]" type="hidden" value="&lt;b&gt;Hello World&lt;/b&gt;" />',
hidden_field("post", "title")
+ )
end
def test_hidden_field_with_nil_value
expected = '<input id="post_title" name="post[title]" type="hidden" />'
- assert_dom_equal expected, hidden_field("post", "title", :value => nil)
+ assert_dom_equal expected, hidden_field("post", "title", value: nil)
end
def test_hidden_field_with_options
- assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="Something Else" />',
- hidden_field("post", "title", :value => "Something Else")
+ assert_dom_equal(
+ '<input id="post_title" name="post[title]" type="hidden" value="Something Else" />',
+ hidden_field("post", "title", value: "Something Else")
+ )
end
def test_text_field_with_custom_type
- assert_dom_equal '<input id="user_email" name="user[email]" type="email" />',
- text_field("user", "email", :type => "email")
+ assert_dom_equal(
+ '<input id="user_email" name="user[email]" type="email" />',
+ text_field("user", "email", type: "email")
+ )
end
def test_check_box_is_html_safe
@@ -371,7 +421,7 @@ class FormHelperTest < ActionView::TestCase
def test_check_box_checked_if_option_checked_is_present
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
- check_box("post", "secret" ,{"checked"=>"checked"})
+ check_box("post", "secret", "checked"=>"checked")
)
end
@@ -410,7 +460,10 @@ class FormHelperTest < ActionView::TestCase
def test_check_box_with_include_hidden_false
@post.secret = false
- assert_dom_equal('<input id="post_secret" name="post[secret]" type="checkbox" value="1" />', check_box("post", "secret", :include_hidden => false))
+ assert_dom_equal(
+ '<input id="post_secret" name="post[secret]" type="checkbox" value="1" />',
+ check_box("post", "secret", include_hidden: false)
+ )
end
def test_check_box_with_explicit_checked_and_unchecked_values_when_object_value_is_string
@@ -517,11 +570,11 @@ class FormHelperTest < ActionView::TestCase
@post.comment_ids = [2,3]
assert_dom_equal(
'<input name="post[comment_ids][]" type="hidden" value="0" /><input id="post_comment_ids_1" name="post[comment_ids][]" type="checkbox" value="1" />',
- check_box("post", "comment_ids", { :multiple => true }, 1)
+ check_box("post", "comment_ids", { multiple: true }, 1)
)
assert_dom_equal(
'<input name="post[comment_ids][]" type="hidden" value="0" /><input checked="checked" id="post_comment_ids_3" name="post[comment_ids][]" type="checkbox" value="3" />',
- check_box("post", "comment_ids", { :multiple => true }, 3)
+ check_box("post", "comment_ids", { multiple: true }, 3)
)
end
@@ -529,11 +582,11 @@ class FormHelperTest < ActionView::TestCase
@post.comment_ids = [2,3]
assert_dom_equal(
'<input name="post[foo][comment_ids][]" type="hidden" value="0" /><input id="post_foo_comment_ids_1" name="post[foo][comment_ids][]" type="checkbox" value="1" />',
- check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1)
+ check_box("post", "comment_ids", { multiple: true, index: "foo" }, 1)
)
assert_dom_equal(
'<input name="post[bar][comment_ids][]" type="hidden" value="0" /><input checked="checked" id="post_bar_comment_ids_3" name="post[bar][comment_ids][]" type="checkbox" value="3" />',
- check_box("post", "comment_ids", { :multiple => true, :index => "bar" }, 3)
+ check_box("post", "comment_ids", { multiple: true, index: "bar" }, 3)
)
end
@@ -541,14 +594,14 @@ class FormHelperTest < ActionView::TestCase
def test_checkbox_disabled_disables_hidden_field
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" disabled="disabled"/><input checked="checked" disabled="disabled" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
- check_box("post", "secret", { :disabled => true })
+ check_box("post", "secret", { disabled: true })
)
end
def test_checkbox_form_html5_attribute
assert_dom_equal(
'<input form="new_form" name="post[secret]" type="hidden" value="0" /><input checked="checked" form="new_form" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
- check_box("post", "secret", :form => "new_form")
+ check_box("post", "secret", form: "new_form")
)
end
@@ -577,7 +630,7 @@ class FormHelperTest < ActionView::TestCase
def test_radio_button_respects_passed_in_id
assert_dom_equal('<input checked="checked" id="foo" name="post[secret]" type="radio" value="1" />',
- radio_button("post", "secret", "1", :id=>"foo")
+ radio_button("post", "secret", "1", id: "foo")
)
end
@@ -599,7 +652,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_text_area_with_escapes
- @post.body = "Back to <i>the</i> hill and over it again!"
+ @post.body = "Back to <i>the</i> hill and over it again!"
assert_dom_equal(
%{<textarea id="post_body" name="post[body]">\nBack to &lt;i&gt;the&lt;/i&gt; hill and over it again!</textarea>},
text_area("post", "body")
@@ -609,12 +662,12 @@ class FormHelperTest < ActionView::TestCase
def test_text_area_with_alternate_value
assert_dom_equal(
%{<textarea id="post_body" name="post[body]">\nTesting alternate values.</textarea>},
- text_area("post", "body", :value => 'Testing alternate values.')
+ text_area("post", "body", value: "Testing alternate values.")
)
end
def test_text_area_with_html_entities
- @post.body = "The HTML Entity for & is &amp;"
+ @post.body = "The HTML Entity for & is &amp;"
assert_dom_equal(
%{<textarea id="post_body" name="post[body]">\nThe HTML Entity for &amp; is &amp;amp;</textarea>},
text_area("post", "body")
@@ -624,7 +677,7 @@ class FormHelperTest < ActionView::TestCase
def test_text_area_with_size_option
assert_dom_equal(
%{<textarea cols="183" id="post_body" name="post[body]" rows="820">\nBack to the hill and over it again!</textarea>},
- text_area("post", "body", :size => "183x820")
+ text_area("post", "body", size: "183x820")
)
end
@@ -666,7 +719,7 @@ class FormHelperTest < ActionView::TestCase
min_value = DateTime.new(2000, 6, 15)
max_value = DateTime.new(2010, 8, 15)
step = 2
- assert_dom_equal(expected, date_field("post", "written_on", :min => min_value, :max => max_value, :step => step))
+ assert_dom_equal(expected, date_field("post", "written_on", min: min_value, max: max_value, step: step))
end
def test_date_field_with_timewithzone_value
@@ -701,7 +754,7 @@ class FormHelperTest < ActionView::TestCase
min_value = DateTime.new(2000, 6, 15, 20, 45, 30)
max_value = DateTime.new(2010, 8, 15, 10, 25, 00)
step = 60
- assert_dom_equal(expected, time_field("post", "written_on", :min => min_value, :max => max_value, :step => step))
+ assert_dom_equal(expected, time_field("post", "written_on", min: min_value, max: max_value, step: step))
end
def test_time_field_with_timewithzone_value
@@ -736,7 +789,7 @@ class FormHelperTest < ActionView::TestCase
min_value = DateTime.new(2000, 6, 15, 20, 45, 30)
max_value = DateTime.new(2010, 8, 15, 10, 25, 00)
step = 60
- assert_dom_equal(expected, datetime_field("post", "written_on", :min => min_value, :max => max_value, :step => step))
+ assert_dom_equal(expected, datetime_field("post", "written_on", min: min_value, max: max_value, step: step))
end
def test_datetime_field_with_timewithzone_value
@@ -771,7 +824,7 @@ class FormHelperTest < ActionView::TestCase
min_value = DateTime.new(2000, 6, 15, 20, 45, 30)
max_value = DateTime.new(2010, 8, 15, 10, 25, 00)
step = 60
- assert_dom_equal(expected, datetime_local_field("post", "written_on", :min => min_value, :max => max_value, :step => step))
+ assert_dom_equal(expected, datetime_local_field("post", "written_on", min: min_value, max: max_value, step: step))
end
def test_datetime_local_field_with_timewithzone_value
@@ -812,7 +865,7 @@ class FormHelperTest < ActionView::TestCase
min_value = DateTime.new(2000, 2, 13)
max_value = DateTime.new(2010, 12, 23)
step = 2
- assert_dom_equal(expected, month_field("post", "written_on", :min => min_value, :max => max_value, :step => step))
+ assert_dom_equal(expected, month_field("post", "written_on", min: min_value, max: max_value, step: step))
end
def test_month_field_with_timewithzone_value
@@ -847,7 +900,7 @@ class FormHelperTest < ActionView::TestCase
min_value = DateTime.new(2000, 2, 13)
max_value = DateTime.new(2010, 12, 23)
step = 2
- assert_dom_equal(expected, week_field("post", "written_on", :min => min_value, :max => max_value, :step => step))
+ assert_dom_equal(expected, week_field("post", "written_on", min: min_value, max: max_value, step: step))
end
def test_week_field_with_timewithzone_value
@@ -871,21 +924,22 @@ class FormHelperTest < ActionView::TestCase
def test_number_field
expected = %{<input name="order[quantity]" max="9" id="order_quantity" type="number" min="1" />}
- assert_dom_equal(expected, number_field("order", "quantity", :in => 1...10))
+ assert_dom_equal(expected, number_field("order", "quantity", in: 1...10))
expected = %{<input name="order[quantity]" size="30" max="9" id="order_quantity" type="number" min="1" />}
- assert_dom_equal(expected, number_field("order", "quantity", :size => 30, :in => 1...10))
+ assert_dom_equal(expected, number_field("order", "quantity", size: 30, in: 1...10))
end
def test_range_input
expected = %{<input name="hifi[volume]" step="0.1" max="11" id="hifi_volume" type="range" min="0" />}
- assert_dom_equal(expected, range_field("hifi", "volume", :in => 0..11, :step => 0.1))
+ assert_dom_equal(expected, range_field("hifi", "volume", in: 0..11, step: 0.1))
expected = %{<input name="hifi[volume]" step="0.1" size="30" max="11" id="hifi_volume" type="range" min="0" />}
- assert_dom_equal(expected, range_field("hifi", "volume", :size => 30, :in => 0..11, :step => 0.1))
+ assert_dom_equal(expected, range_field("hifi", "volume", size: 30, in: 0..11, step: 0.1))
end
def test_explicit_name
assert_dom_equal(
- '<input id="post_title" name="dont guess" type="text" value="Hello World" />', text_field("post", "title", "name" => "dont guess")
+ '<input id="post_title" name="dont guess" type="text" value="Hello World" />',
+ text_field("post", "title", "name" => "dont guess")
)
assert_dom_equal(
%{<textarea id="post_body" name="really!">\nBack to the hill and over it again!</textarea>},
@@ -895,17 +949,24 @@ class FormHelperTest < ActionView::TestCase
'<input name="i mean it" type="hidden" value="0" /><input checked="checked" id="post_secret" name="i mean it" type="checkbox" value="1" />',
check_box("post", "secret", "name" => "i mean it")
)
- assert_dom_equal text_field("post", "title", "name" => "dont guess"),
- text_field("post", "title", :name => "dont guess")
- assert_dom_equal text_area("post", "body", "name" => "really!"),
- text_area("post", "body", :name => "really!")
- assert_dom_equal check_box("post", "secret", "name" => "i mean it"),
- check_box("post", "secret", :name => "i mean it")
+ assert_dom_equal(
+ text_field("post", "title", "name" => "dont guess"),
+ text_field("post", "title", name: "dont guess")
+ )
+ assert_dom_equal(
+ text_area("post", "body", "name" => "really!"),
+ text_area("post", "body", name: "really!")
+ )
+ assert_dom_equal(
+ check_box("post", "secret", "name" => "i mean it"),
+ check_box("post", "secret", name: "i mean it")
+ )
end
def test_explicit_id
assert_dom_equal(
- '<input id="dont guess" name="post[title]" type="text" value="Hello World" />', text_field("post", "title", "id" => "dont guess")
+ '<input id="dont guess" name="post[title]" type="text" value="Hello World" />',
+ text_field("post", "title", "id" => "dont guess")
)
assert_dom_equal(
%{<textarea id="really!" name="post[body]">\nBack to the hill and over it again!</textarea>},
@@ -915,17 +976,24 @@ class FormHelperTest < ActionView::TestCase
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="i mean it" name="post[secret]" type="checkbox" value="1" />',
check_box("post", "secret", "id" => "i mean it")
)
- assert_dom_equal text_field("post", "title", "id" => "dont guess"),
- text_field("post", "title", :id => "dont guess")
- assert_dom_equal text_area("post", "body", "id" => "really!"),
- text_area("post", "body", :id => "really!")
- assert_dom_equal check_box("post", "secret", "id" => "i mean it"),
- check_box("post", "secret", :id => "i mean it")
+ assert_dom_equal(
+ text_field("post", "title", "id" => "dont guess"),
+ text_field("post", "title", id: "dont guess")
+ )
+ assert_dom_equal(
+ text_area("post", "body", "id" => "really!"),
+ text_area("post", "body", id: "really!")
+ )
+ assert_dom_equal(
+ check_box("post", "secret", "id" => "i mean it"),
+ check_box("post", "secret", id: "i mean it")
+ )
end
def test_nil_id
assert_dom_equal(
- '<input name="post[title]" type="text" value="Hello World" />', text_field("post", "title", "id" => nil)
+ '<input name="post[title]" type="text" value="Hello World" />',
+ text_field("post", "title", "id" => nil)
)
assert_dom_equal(
%{<textarea name="post[body]">\nBack to the hill and over it again!</textarea>},
@@ -943,14 +1011,22 @@ class FormHelperTest < ActionView::TestCase
'<select name="post[secret]"></select>',
select("post", "secret", [], {}, "id" => nil)
)
- assert_dom_equal text_field("post", "title", "id" => nil),
- text_field("post", "title", :id => nil)
- assert_dom_equal text_area("post", "body", "id" => nil),
- text_area("post", "body", :id => nil)
- assert_dom_equal check_box("post", "secret", "id" => nil),
- check_box("post", "secret", :id => nil)
- assert_dom_equal radio_button("post", "secret", "0", "id" => nil),
- radio_button("post", "secret", "0", :id => nil)
+ assert_dom_equal(
+ text_field("post", "title", "id" => nil),
+ text_field("post", "title", id: nil)
+ )
+ assert_dom_equal(
+ text_area("post", "body", "id" => nil),
+ text_area("post", "body", id: nil)
+ )
+ assert_dom_equal(
+ check_box("post", "secret", "id" => nil),
+ check_box("post", "secret", id: nil)
+ )
+ assert_dom_equal(
+ radio_button("post", "secret", "0", "id" => nil),
+ radio_button("post", "secret", "0", id: nil)
+ )
end
def test_index
@@ -995,40 +1071,42 @@ class FormHelperTest < ActionView::TestCase
)
assert_dom_equal(
text_field("post", "title", "index" => 5, 'id' => nil),
- text_field("post", "title", :index => 5, :id => nil)
+ text_field("post", "title", index: 5, id: nil)
)
assert_dom_equal(
text_area("post", "body", "index" => 5, 'id' => nil),
- text_area("post", "body", :index => 5, :id => nil)
+ text_area("post", "body", index: 5, id: nil)
)
assert_dom_equal(
check_box("post", "secret", "index" => 5, 'id' => nil),
- check_box("post", "secret", :index => 5, :id => nil)
+ check_box("post", "secret", index: 5, id: nil)
)
end
def test_auto_index
pid = 123
assert_dom_equal(
- "<label for=\"post_#{pid}_title\">Title</label>",
+ %{<label for="post_#{pid}_title">Title</label>},
label("post[]", "title")
)
assert_dom_equal(
- "<input id=\"post_#{pid}_title\" name=\"post[#{pid}][title]\" type=\"text\" value=\"Hello World\" />", text_field("post[]","title")
+ %{<input id="post_#{pid}_title" name="post[#{pid}][title]" type="text" value="Hello World" />},
+ text_field("post[]","title")
)
assert_dom_equal(
- "<textarea id=\"post_#{pid}_body\" name=\"post[#{pid}][body]\">\nBack to the hill and over it again!</textarea>",
+ %{<textarea id="post_#{pid}_body" name="post[#{pid}][body]">\nBack to the hill and over it again!</textarea>},
text_area("post[]", "body")
)
assert_dom_equal(
- "<input name=\"post[#{pid}][secret]\" type=\"hidden\" value=\"0\" /><input checked=\"checked\" id=\"post_#{pid}_secret\" name=\"post[#{pid}][secret]\" type=\"checkbox\" value=\"1\" />",
+ %{<input name="post[#{pid}][secret]" type="hidden" value="0" /><input checked="checked" id="post_#{pid}_secret" name="post[#{pid}][secret]" type="checkbox" value="1" />},
check_box("post[]", "secret")
)
assert_dom_equal(
- "<input checked=\"checked\" id=\"post_#{pid}_title_hello_world\" name=\"post[#{pid}][title]\" type=\"radio\" value=\"Hello World\" />",
+ %{<input checked="checked" id="post_#{pid}_title_hello_world" name="post[#{pid}][title]" type="radio" value="Hello World" />},
radio_button("post[]", "title", "Hello World")
)
- assert_dom_equal("<input id=\"post_#{pid}_title_goodbye_world\" name=\"post[#{pid}][title]\" type=\"radio\" value=\"Goodbye World\" />",
+ assert_dom_equal(
+ %{<input id="post_#{pid}_title_goodbye_world" name="post[#{pid}][title]" type="radio" value="Goodbye World" />},
radio_button("post[]", "title", "Goodbye World")
)
end
@@ -1036,48 +1114,49 @@ class FormHelperTest < ActionView::TestCase
def test_auto_index_with_nil_id
pid = 123
assert_dom_equal(
- "<input name=\"post[#{pid}][title]\" type=\"text\" value=\"Hello World\" />",
- text_field("post[]","title", :id => nil)
+ %{<input name="post[#{pid}][title]" type="text" value="Hello World" />},
+ text_field("post[]", "title", id: nil)
)
assert_dom_equal(
- "<textarea name=\"post[#{pid}][body]\">\nBack to the hill and over it again!</textarea>",
- text_area("post[]", "body", :id => nil)
+ %{<textarea name="post[#{pid}][body]">\nBack to the hill and over it again!</textarea>},
+ text_area("post[]", "body", id: nil)
)
assert_dom_equal(
- "<input name=\"post[#{pid}][secret]\" type=\"hidden\" value=\"0\" /><input checked=\"checked\" name=\"post[#{pid}][secret]\" type=\"checkbox\" value=\"1\" />",
- check_box("post[]", "secret", :id => nil)
+ %{<input name="post[#{pid}][secret]" type="hidden" value="0" /><input checked="checked" name="post[#{pid}][secret]" type="checkbox" value="1" />},
+ check_box("post[]", "secret", id: nil)
)
assert_dom_equal(
-"<input checked=\"checked\" name=\"post[#{pid}][title]\" type=\"radio\" value=\"Hello World\" />",
- radio_button("post[]", "title", "Hello World", :id => nil)
+ %{<input checked="checked" name="post[#{pid}][title]" type="radio" value="Hello World" />},
+ radio_button("post[]", "title", "Hello World", id: nil)
)
- assert_dom_equal("<input name=\"post[#{pid}][title]\" type=\"radio\" value=\"Goodbye World\" />",
- radio_button("post[]", "title", "Goodbye World", :id => nil)
+ assert_dom_equal(
+ %{<input name="post[#{pid}][title]" type="radio" value="Goodbye World" />},
+ radio_button("post[]", "title", "Goodbye World", id: nil)
)
end
def test_form_for_requires_block
assert_raises(ArgumentError) do
- form_for(:post, @post, :html => { :id => 'create-post' })
+ form_for(:post, @post, html: { id: 'create-post' })
end
end
def test_form_for_requires_arguments
error = assert_raises(ArgumentError) do
- form_for(nil, :html => { :id => 'create-post' }) do
+ form_for(nil, html: { id: 'create-post' }) do
end
end
assert_equal "First argument in form cannot contain nil or be empty", error.message
error = assert_raises(ArgumentError) do
- form_for([nil, nil], :html => { :id => 'create-post' }) do
+ form_for([nil, nil], html: { id: 'create-post' }) do
end
end
assert_equal "First argument in form cannot contain nil or be empty", error.message
end
def test_form_for
- form_for(@post, :html => { :id => 'create-post' }) do |f|
+ form_for(@post, html: { id: 'create-post' }) do |f|
concat f.label(:title) { "The Title" }
concat f.text_field(:title)
concat f.text_area(:body)
@@ -1089,7 +1168,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form("/posts/123", "create-post" , "edit_post", :method => 'patch') do
+ expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do
"<label for='post_title'>The Title</label>" +
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
@@ -1110,7 +1189,7 @@ class FormHelperTest < ActionView::TestCase
concat f.collection_radio_buttons(:active, [true, false], :to_s, :to_s)
end
- expected = whole_form("/posts", "new_post" , "new_post") do
+ expected = whole_form("/posts", "new_post", "new_post") do
"<input id='post_active_true' name='post[active]' type='radio' value='true' />" +
"<label for='post_active_true'>true</label>" +
"<input checked='checked' id='post_active_false' name='post[active]' type='radio' value='false' />" +
@@ -1123,6 +1202,7 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_collection_radio_buttons_with_custom_builder_block
post = Post.new
def post.active; false; end
+
form_for(post) do |f|
rendered_radio_buttons = f.collection_radio_buttons(:active, [true, false], :to_s, :to_s) do |b|
b.label { b.radio_button + b.text }
@@ -1130,7 +1210,7 @@ class FormHelperTest < ActionView::TestCase
concat rendered_radio_buttons
end
- expected = whole_form("/posts", "new_post" , "new_post") do
+ expected = whole_form("/posts", "new_post", "new_post") do
"<label for='post_active_true'>"+
"<input id='post_active_true' name='post[active]' type='radio' value='true' />" +
"true</label>" +
@@ -1142,15 +1222,41 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ def test_form_for_with_collection_radio_buttons_with_custom_builder_block_does_not_leak_the_template
+ post = Post.new
+ def post.active; false; end
+ def post.id; 1; end
+
+ form_for(post) do |f|
+ rendered_radio_buttons = f.collection_radio_buttons(:active, [true, false], :to_s, :to_s) do |b|
+ b.label { b.radio_button + b.text }
+ end
+ concat rendered_radio_buttons
+ concat f.hidden_field :id
+ end
+
+ expected = whole_form("/posts", "new_post_1", "new_post") do
+ "<label for='post_active_true'>"+
+ "<input id='post_active_true' name='post[active]' type='radio' value='true' />" +
+ "true</label>" +
+ "<label for='post_active_false'>"+
+ "<input checked='checked' id='post_active_false' name='post[active]' type='radio' value='false' />" +
+ "false</label>"+
+ "<input id='post_id' name='post[id]' type='hidden' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
def test_form_for_with_collection_check_boxes
post = Post.new
def post.tag_ids; [1, 3]; end
- collection = (1..3).map{|i| [i, "Tag #{i}"] }
+ collection = (1..3).map { |i| [i, "Tag #{i}"] }
form_for(post) do |f|
concat f.collection_check_boxes(:tag_ids, collection, :first, :last)
end
- expected = whole_form("/posts", "new_post" , "new_post") do
+ expected = whole_form("/posts", "new_post", "new_post") do
"<input checked='checked' id='post_tag_ids_1' name='post[tag_ids][]' type='checkbox' value='1' />" +
"<label for='post_tag_ids_1'>Tag 1</label>" +
"<input id='post_tag_ids_2' name='post[tag_ids][]' type='checkbox' value='2' />" +
@@ -1166,7 +1272,7 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_collection_check_boxes_with_custom_builder_block
post = Post.new
def post.tag_ids; [1, 3]; end
- collection = (1..3).map{|i| [i, "Tag #{i}"] }
+ collection = (1..3).map { |i| [i, "Tag #{i}"] }
form_for(post) do |f|
rendered_check_boxes = f.collection_check_boxes(:tag_ids, collection, :first, :last) do |b|
b.label { b.check_box + b.text }
@@ -1174,7 +1280,7 @@ class FormHelperTest < ActionView::TestCase
concat rendered_check_boxes
end
- expected = whole_form("/posts", "new_post" , "new_post") do
+ expected = whole_form("/posts", "new_post", "new_post") do
"<label for='post_tag_ids_1'>" +
"<input checked='checked' id='post_tag_ids_1' name='post[tag_ids][]' type='checkbox' value='1' />" +
"Tag 1</label>" +
@@ -1190,14 +1296,45 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ def test_form_for_with_collection_check_boxes_with_custom_builder_block_does_not_leak_the_template
+ post = Post.new
+ def post.tag_ids; [1, 3]; end
+ def post.id; 1; end
+ collection = (1..3).map { |i| [i, "Tag #{i}"] }
+
+ form_for(post) do |f|
+ rendered_check_boxes = f.collection_check_boxes(:tag_ids, collection, :first, :last) do |b|
+ b.label { b.check_box + b.text }
+ end
+ concat rendered_check_boxes
+ concat f.hidden_field :id
+ end
+
+ expected = whole_form("/posts", "new_post_1", "new_post") do
+ "<label for='post_tag_ids_1'>" +
+ "<input checked='checked' id='post_tag_ids_1' name='post[tag_ids][]' type='checkbox' value='1' />" +
+ "Tag 1</label>" +
+ "<label for='post_tag_ids_2'>" +
+ "<input id='post_tag_ids_2' name='post[tag_ids][]' type='checkbox' value='2' />" +
+ "Tag 2</label>" +
+ "<label for='post_tag_ids_3'>" +
+ "<input checked='checked' id='post_tag_ids_3' name='post[tag_ids][]' type='checkbox' value='3' />" +
+ "Tag 3</label>" +
+ "<input name='post[tag_ids][]' type='hidden' value='' />"+
+ "<input id='post_id' name='post[id]' type='hidden' value='1' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
def test_form_for_with_file_field_generate_multipart
Post.send :attr_accessor, :file
- form_for(@post, :html => { :id => 'create-post' }) do |f|
+ form_for(@post, html: { id: 'create-post' }) do |f|
concat f.file_field(:file)
end
- expected = whole_form("/posts/123", "create-post" , "edit_post", :method => 'patch', :multipart => true) do
+ expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch", multipart: true) do
"<input name='post[file]' type='file' id='post_file' />"
end
@@ -1213,7 +1350,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form("/posts/123", "edit_post_123" , "edit_post", :method => 'patch', :multipart => true) do
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", method: "patch", multipart: true) do
"<input name='post[comment][file]' type='file' id='post_comment_file' />"
end
@@ -1221,11 +1358,11 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_format
- form_for(@post, :format => :json, :html => { :id => "edit_post_123", :class => "edit_post" }) do |f|
+ form_for(@post, format: :json, html: { id: "edit_post_123", class: "edit_post" }) do |f|
concat f.label(:title)
end
- expected = whole_form("/posts/123.json", "edit_post_123" , "edit_post", :method => 'patch') do
+ expected = whole_form("/posts/123.json", "edit_post_123", "edit_post", method: 'patch') do
"<label for='post_title'>Title</label>"
end
@@ -1240,7 +1377,7 @@ class FormHelperTest < ActionView::TestCase
concat f.submit('Edit post')
end
- expected = whole_form("/posts/44", "edit_post_44" , "edit_post", :method => 'patch') do
+ expected = whole_form("/posts/44", "edit_post_44", "edit_post", method: "patch") do
"<input name='post[title]' type='text' id='post_title' value='And his name will be forty and four.' />" +
"<input name='commit' type='submit' value='Edit post' />"
end
@@ -1249,15 +1386,15 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_symbol_object_name
- form_for(@post, :as => "other_name", :html => { :id => 'create-post' }) do |f|
- concat f.label(:title, :class => 'post_title')
+ form_for(@post, as: "other_name", html: { id: "create-post" }) do |f|
+ concat f.label(:title, class: 'post_title')
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
concat f.submit('Create post')
end
- expected = whole_form("/posts/123", "create-post", "edit_other_name", :method => 'patch') do
+ expected = whole_form("/posts/123", "create-post", "edit_other_name", method: "patch") do
"<label for='other_name_title' class='post_title'>Title</label>" +
"<input name='other_name[title]' id='other_name_title' value='Hello World' type='text' />" +
"<textarea name='other_name[body]' id='other_name_body'>\nBack to the hill and over it again!</textarea>" +
@@ -1270,13 +1407,13 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_method_as_part_of_html_options
- form_for(@post, :url => '/', :html => { :id => 'create-post', :method => :delete }) do |f|
+ form_for(@post, url: '/', html: { id: 'create-post', method: :delete }) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
end
- expected = whole_form("/", "create-post", "edit_post", "delete") do
+ expected = whole_form("/", "create-post", "edit_post", method: "delete") do
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
@@ -1287,13 +1424,13 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_method
- form_for(@post, :url => '/', :method => :delete, :html => { :id => 'create-post' }) do |f|
+ form_for(@post, url: '/', method: :delete, html: { id: 'create-post' }) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
end
- expected = whole_form("/", "create-post", "edit_post", "delete") do
+ expected = whole_form("/", "create-post", "edit_post", method: "delete") do
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
@@ -1306,11 +1443,11 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_search_field
# Test case for bug which would emit an "object" attribute
# when used with form_for using a search_field form helper
- form_for(Post.new, :url => "/search", :html => { :id => 'search-post', :method => :get}) do |f|
+ form_for(Post.new, url: "/search", html: { id: "search-post", method: :get }) do |f|
concat f.search_field(:title)
end
- expected = whole_form("/search", "search-post", "new_post", "get") do
+ expected = whole_form("/search", "search-post", "new_post", method: "get") do
"<input name='post[title]' type='search' id='post_title' />"
end
@@ -1318,13 +1455,13 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_remote
- form_for(@post, :url => '/', :remote => true, :html => { :id => 'create-post', :method => :patch}) do |f|
+ form_for(@post, url: '/', remote: true, html: { id: 'create-post', method: :patch }) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
end
- expected = whole_form("/", "create-post", "edit_post", :method => 'patch', :remote => true) do
+ expected = whole_form("/", "create-post", "edit_post", method: "patch", remote: true) do
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
@@ -1335,13 +1472,13 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_remote_in_html
- form_for(@post, :url => '/', :html => { :remote => true, :id => 'create-post', :method => :patch }) do |f|
+ form_for(@post, url: '/', html: { remote: true, id: 'create-post', method: :patch }) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
end
- expected = whole_form("/", "create-post", "edit_post", :method => 'patch', :remote => true) do
+ expected = whole_form("/", "create-post", "edit_post", method: "patch", remote: true) do
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
@@ -1354,13 +1491,13 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_remote_without_html
@post.persisted = false
@post.stubs(:to_key).returns(nil)
- form_for(@post, :remote => true) do |f|
+ form_for(@post, remote: true) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
end
- expected = whole_form("/posts", 'new_post', 'new_post', :remote => true) do
+ expected = whole_form("/posts", "new_post", "new_post", remote: true) do
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
@@ -1371,7 +1508,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_without_object
- form_for(:post, :html => { :id => 'create-post' }) do |f|
+ form_for(:post, html: { id: 'create-post' }) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
@@ -1388,14 +1525,14 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_index
- form_for(@post, :as => "post[]") do |f|
+ form_for(@post, as: "post[]") do |f|
concat f.label(:title)
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
end
- expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', method: 'patch') do
"<label for='post_123_title'>Title</label>" +
"<input name='post[123][title]' type='text' id='post_123_title' value='Hello World' />" +
"<textarea name='post[123][body]' id='post_123_body'>\nBack to the hill and over it again!</textarea>" +
@@ -1407,13 +1544,13 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_nil_index_option_override
- form_for(@post, :as => "post[]", :index => nil) do |f|
+ form_for(@post, as: "post[]", index: nil) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
end
- expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', method: 'patch') do
"<input name='post[][title]' type='text' id='post__title' value='Hello World' />" +
"<textarea name='post[][body]' id='post__body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[][secret]' type='hidden' value='0' />" +
@@ -1425,12 +1562,12 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_label_error_wrapping
form_for(@post) do |f|
- concat f.label(:author_name, :class => 'label')
+ concat f.label(:author_name, class: 'label')
concat f.text_field(:author_name)
concat f.submit('Create post')
end
- expected = whole_form('/posts/123', 'edit_post_123' , 'edit_post', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" +
"<div class='field_with_errors'><input name='post[author_name]' type='text' id='post_author_name' value='' /></div>" +
"<input name='commit' type='submit' value='Create post' />"
@@ -1443,12 +1580,12 @@ class FormHelperTest < ActionView::TestCase
post = remove_instance_variable :@post
form_for(post) do |f|
- concat f.label(:author_name, :class => 'label')
+ concat f.label(:author_name, class: 'label')
concat f.text_field(:author_name)
concat f.submit('Create post')
end
- expected = whole_form('/posts/123', 'edit_post_123' , 'edit_post', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" +
"<div class='field_with_errors'><input name='post[author_name]' type='text' id='post_author_name' value='' /></div>" +
"<input name='commit' type='submit' value='Create post' />"
@@ -1459,11 +1596,11 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_label_error_wrapping_block_and_non_block_versions
form_for(@post) do |f|
- concat f.label(:author_name, 'Name', :class => 'label')
- concat f.label(:author_name, :class => 'label') { 'Name' }
+ concat f.label(:author_name, 'Name', class: 'label')
+ concat f.label(:author_name, class: 'label') { 'Name' }
end
- expected = whole_form('/posts/123', 'edit_post_123' , 'edit_post', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<div class='field_with_errors'><label for='post_author_name' class='label'>Name</label></div>" +
"<div class='field_with_errors'><label for='post_author_name' class='label'>Name</label></div>"
end
@@ -1472,13 +1609,13 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_namespace
- form_for(@post, :namespace => 'namespace') do |f|
+ form_for(@post, namespace: 'namespace') do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
end
- expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', 'patch') do
+ expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', method: 'patch') do
"<input name='post[title]' type='text' id='namespace_post_title' value='Hello World' />" +
"<textarea name='post[body]' id='namespace_post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[secret]' type='hidden' value='0' />" +
@@ -1489,7 +1626,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_namespace_with_date_select
- form_for(@post, :namespace => 'namespace') do |f|
+ form_for(@post, namespace: 'namespace') do |f|
concat f.date_select(:written_on)
end
@@ -1497,12 +1634,12 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_namespace_with_label
- form_for(@post, :namespace => 'namespace') do |f|
+ form_for(@post, namespace: 'namespace') do |f|
concat f.label(:title)
concat f.text_field(:title)
end
- expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', 'patch') do
+ expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', method: 'patch') do
"<label for='namespace_post_title'>Title</label>" +
"<input name='post[title]' type='text' id='namespace_post_title' value='Hello World' />"
end
@@ -1511,24 +1648,24 @@ class FormHelperTest < ActionView::TestCase
end
def test_two_form_for_with_namespace
- form_for(@post, :namespace => 'namespace_1') do |f|
+ form_for(@post, namespace: 'namespace_1') do |f|
concat f.label(:title)
concat f.text_field(:title)
end
- expected_1 = whole_form('/posts/123', 'namespace_1_edit_post_123', 'edit_post', 'patch') do
+ expected_1 = whole_form('/posts/123', 'namespace_1_edit_post_123', 'edit_post', method: 'patch') do
"<label for='namespace_1_post_title'>Title</label>" +
"<input name='post[title]' type='text' id='namespace_1_post_title' value='Hello World' />"
end
assert_dom_equal expected_1, output_buffer
- form_for(@post, :namespace => 'namespace_2') do |f|
+ form_for(@post, namespace: 'namespace_2') do |f|
concat f.label(:title)
concat f.text_field(:title)
end
- expected_2 = whole_form('/posts/123', 'namespace_2_edit_post_123', 'edit_post', 'patch') do
+ expected_2 = whole_form('/posts/123', 'namespace_2_edit_post_123', 'edit_post', method: 'patch') do
"<label for='namespace_2_post_title'>Title</label>" +
"<input name='post[title]' type='text' id='namespace_2_post_title' value='Hello World' />"
end
@@ -1538,7 +1675,7 @@ class FormHelperTest < ActionView::TestCase
def test_fields_for_with_namespace
@comment.body = 'Hello World'
- form_for(@post, :namespace => 'namespace') do |f|
+ form_for(@post, namespace: 'namespace') do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.fields_for(@comment) { |c|
@@ -1546,7 +1683,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', 'patch') do
+ expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', method: 'patch') do
"<input name='post[title]' type='text' id='namespace_post_title' value='Hello World' />" +
"<textarea name='post[body]' id='namespace_post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[comment][body]' type='text' id='namespace_post_comment_body' value='Hello World' />"
@@ -1580,7 +1717,7 @@ class FormHelperTest < ActionView::TestCase
concat f.submit
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<input name='commit' type='submit' value='Confirm Post changes' />"
end
@@ -1593,7 +1730,7 @@ class FormHelperTest < ActionView::TestCase
old_locale, I18n.locale = I18n.locale, :submit
form_for(:post) do |f|
- concat f.submit :class => "extra"
+ concat f.submit class: "extra"
end
expected = whole_form do
@@ -1608,11 +1745,11 @@ class FormHelperTest < ActionView::TestCase
def test_submit_with_object_and_nested_lookup
old_locale, I18n.locale = I18n.locale, :submit
- form_for(@post, :as => :another_post) do |f|
+ form_for(@post, as: :another_post) do |f|
concat f.submit
end
- expected = whole_form('/posts/123', 'edit_another_post', 'edit_another_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_another_post', 'edit_another_post', method: 'patch') do
"<input name='commit' type='submit' value='Update your Post' />"
end
@@ -1629,7 +1766,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<input name='post[comment][body]' type='text' id='post_comment_body' value='Hello World' />"
end
@@ -1637,14 +1774,14 @@ class FormHelperTest < ActionView::TestCase
end
def test_nested_fields_for_with_nested_collections
- form_for(@post, :as => 'post[]') do |f|
+ form_for(@post, as: 'post[]') do |f|
concat f.text_field(:title)
concat f.fields_for('comment[]', @comment) { |c|
concat c.text_field(:name)
}
end
- expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', method: 'patch') do
"<input name='post[123][title]' type='text' id='post_123_title' value='Hello World' />" +
"<input name='post[123][comment][][name]' type='text' id='post_123_comment__name' value='new comment' />"
end
@@ -1653,14 +1790,14 @@ class FormHelperTest < ActionView::TestCase
end
def test_nested_fields_for_with_index_and_parent_fields
- form_for(@post, :index => 1) do |c|
+ form_for(@post, index: 1) do |c|
concat c.text_field(:title)
- concat c.fields_for('comment', @comment, :index => 1) { |r|
+ concat c.fields_for('comment', @comment, index: 1) { |r|
concat r.text_field(:name)
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<input name='post[1][title]' type='text' id='post_1_title' value='Hello World' />" +
"<input name='post[1][comment][1][name]' type='text' id='post_1_comment_1_name' value='new comment' />"
end
@@ -1669,13 +1806,13 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_index_and_nested_fields_for
- output_buffer = form_for(@post, :index => 1) do |f|
+ output_buffer = form_for(@post, index: 1) do |f|
concat f.fields_for(:comment, @post) { |c|
concat c.text_field(:title)
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<input name='post[1][comment][title]' type='text' id='post_1_comment_title' value='Hello World' />"
end
@@ -1683,13 +1820,13 @@ class FormHelperTest < ActionView::TestCase
end
def test_nested_fields_for_with_index_on_both
- form_for(@post, :index => 1) do |f|
- concat f.fields_for(:comment, @post, :index => 5) { |c|
+ form_for(@post, index: 1) do |f|
+ concat f.fields_for(:comment, @post, index: 5) { |c|
concat c.text_field(:title)
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<input name='post[1][comment][5][title]' type='text' id='post_1_comment_5_title' value='Hello World' />"
end
@@ -1697,13 +1834,13 @@ class FormHelperTest < ActionView::TestCase
end
def test_nested_fields_for_with_auto_index
- form_for(@post, :as => "post[]") do |f|
+ form_for(@post, as: "post[]") do |f|
concat f.fields_for(:comment, @post) { |c|
concat c.text_field(:title)
}
end
- expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', method: 'patch') do
"<input name='post[123][comment][title]' type='text' id='post_123_comment_title' value='Hello World' />"
end
@@ -1712,12 +1849,12 @@ class FormHelperTest < ActionView::TestCase
def test_nested_fields_for_with_index_radio_button
form_for(@post) do |f|
- concat f.fields_for(:comment, @post, :index => 5) { |c|
+ concat f.fields_for(:comment, @post, index: 5) { |c|
concat c.radio_button(:title, "hello")
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<input name='post[comment][5][title]' type='radio' id='post_comment_5_title_hello' value='hello' />"
end
@@ -1725,13 +1862,13 @@ class FormHelperTest < ActionView::TestCase
end
def test_nested_fields_for_with_auto_index_on_both
- form_for(@post, :as => "post[]") do |f|
+ form_for(@post, as: "post[]") do |f|
concat f.fields_for("comment[]", @post) { |c|
concat c.text_field(:title)
}
end
- expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', method: 'patch') do
"<input name='post[123][comment][123][title]' type='text' id='post_123_comment_123_title' value='Hello World' />"
end
@@ -1739,21 +1876,21 @@ class FormHelperTest < ActionView::TestCase
end
def test_nested_fields_for_with_index_and_auto_index
- output_buffer = form_for(@post, :as => "post[]") do |f|
- concat f.fields_for(:comment, @post, :index => 5) { |c|
+ output_buffer = form_for(@post, as: "post[]") do |f|
+ concat f.fields_for(:comment, @post, index: 5) { |c|
concat c.text_field(:title)
}
end
- output_buffer << form_for(@post, :as => :post, :index => 1) do |f|
+ output_buffer << form_for(@post, as: :post, index: 1) do |f|
concat f.fields_for("comment[]", @post) { |c|
concat c.text_field(:title)
}
end
- expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', method: 'patch') do
"<input name='post[123][comment][5][title]' type='text' id='post_123_comment_5_title' value='Hello World' />"
- end + whole_form('/posts/123', 'edit_post', 'edit_post', 'patch') do
+ end + whole_form('/posts/123', 'edit_post', 'edit_post', method: 'patch') do
"<input name='post[1][comment][123][title]' type='text' id='post_1_comment_123_title' value='Hello World' />"
end
@@ -1770,7 +1907,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="new author" />'
end
@@ -1797,7 +1934,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' +
'<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />'
@@ -1816,7 +1953,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' +
'<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />'
@@ -1830,12 +1967,12 @@ class FormHelperTest < ActionView::TestCase
form_for(@post) do |f|
concat f.text_field(:title)
- concat f.fields_for(:author, :include_id => false) { |af|
+ concat f.fields_for(:author, include_id: false) { |af|
af.text_field(:name)
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />'
end
@@ -1846,14 +1983,14 @@ class FormHelperTest < ActionView::TestCase
def test_nested_fields_for_with_an_existing_record_on_a_nested_attributes_one_to_one_association_with_disabled_hidden_id_inherited
@post.author = Author.new(321)
- form_for(@post, :include_id => false) do |f|
+ form_for(@post, include_id: false) do |f|
concat f.text_field(:title)
concat f.fields_for(:author) { |af|
af.text_field(:name)
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />'
end
@@ -1864,14 +2001,14 @@ class FormHelperTest < ActionView::TestCase
def test_nested_fields_for_with_an_existing_record_on_a_nested_attributes_one_to_one_association_with_disabled_hidden_id_override
@post.author = Author.new(321)
- form_for(@post, :include_id => false) do |f|
+ form_for(@post, include_id: false) do |f|
concat f.text_field(:title)
- concat f.fields_for(:author, :include_id => true) { |af|
+ concat f.fields_for(:author, include_id: true) { |af|
af.text_field(:name)
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' +
'<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />'
@@ -1891,7 +2028,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' +
'<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />'
@@ -1912,7 +2049,7 @@ class FormHelperTest < ActionView::TestCase
end
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
@@ -1933,13 +2070,13 @@ class FormHelperTest < ActionView::TestCase
concat af.text_field(:name)
}
@post.comments.each do |comment|
- concat f.fields_for(:comments, comment, :include_id => false) { |cf|
+ concat f.fields_for(:comments, comment, include_id: false) { |cf|
concat cf.text_field(:name)
}
end
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' +
'<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' +
@@ -1954,7 +2091,7 @@ class FormHelperTest < ActionView::TestCase
@post.comments = Array.new(2) { |id| Comment.new(id + 1) }
@post.author = Author.new(321)
- form_for(@post, :include_id => false) do |f|
+ form_for(@post, include_id: false) do |f|
concat f.text_field(:title)
concat f.fields_for(:author) { |af|
concat af.text_field(:name)
@@ -1966,7 +2103,7 @@ class FormHelperTest < ActionView::TestCase
end
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' +
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
@@ -1980,9 +2117,9 @@ class FormHelperTest < ActionView::TestCase
@post.comments = Array.new(2) { |id| Comment.new(id + 1) }
@post.author = Author.new(321)
- form_for(@post, :include_id => false) do |f|
+ form_for(@post, include_id: false) do |f|
concat f.text_field(:title)
- concat f.fields_for(:author, :include_id => true) { |af|
+ concat f.fields_for(:author, include_id: true) { |af|
concat af.text_field(:name)
}
@post.comments.each do |comment|
@@ -1992,7 +2129,7 @@ class FormHelperTest < ActionView::TestCase
end
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' +
'<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' +
@@ -2015,7 +2152,7 @@ class FormHelperTest < ActionView::TestCase
end
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
@@ -2039,7 +2176,7 @@ class FormHelperTest < ActionView::TestCase
end
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
@@ -2062,7 +2199,7 @@ class FormHelperTest < ActionView::TestCase
end
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="new comment" />' +
'<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="new comment" />'
@@ -2083,7 +2220,7 @@ class FormHelperTest < ActionView::TestCase
end
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #321" />' +
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
@@ -2101,7 +2238,7 @@ class FormHelperTest < ActionView::TestCase
end
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />'
end
@@ -2118,7 +2255,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
@@ -2139,7 +2276,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
@@ -2161,7 +2298,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' +
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
@@ -2184,7 +2321,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input name="post[title]" type="text" id="post_title" value="Hello World" />' +
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #321" />' +
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
@@ -2199,12 +2336,12 @@ class FormHelperTest < ActionView::TestCase
@post.comments = []
form_for(@post) do |f|
- concat f.fields_for(:comments, Comment.new(321), :child_index => 'abc') { |cf|
+ concat f.fields_for(:comments, Comment.new(321), child_index: 'abc') { |cf|
concat cf.text_field(:name)
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input id="post_comments_attributes_abc_name" name="post[comments_attributes][abc][name]" type="text" value="comment #321" />' +
'<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />'
end
@@ -2222,12 +2359,12 @@ class FormHelperTest < ActionView::TestCase
@post.comments = FakeAssociationProxy.new
form_for(@post) do |f|
- concat f.fields_for(:comments, Comment.new(321), :child_index => 'abc') { |cf|
+ concat f.fields_for(:comments, Comment.new(321), child_index: 'abc') { |cf|
concat cf.text_field(:name)
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input id="post_comments_attributes_abc_name" name="post[comments_attributes][abc][name]" type="text" value="comment #321" />' +
'<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />'
end
@@ -2279,7 +2416,7 @@ class FormHelperTest < ActionView::TestCase
@post.comments = []
form_for(@post) do |f|
- f.fields_for(:comments, Comment.new(321), :child_index => 'abc') { |cf|
+ f.fields_for(:comments, Comment.new(321), child_index: 'abc') { |cf|
assert_equal cf.index, 'abc'
}
end
@@ -2313,7 +2450,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #321" />' +
'<input id="post_comments_attributes_0_relevances_attributes_0_value" name="post[comments_attributes][0][relevances_attributes][0][value]" type="text" value="commentrelevance #314" />' +
'<input id="post_comments_attributes_0_relevances_attributes_0_id" name="post[comments_attributes][0][relevances_attributes][0][id]" type="hidden" value="314" />' +
@@ -2340,7 +2477,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
'<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="hash backed author" />'
end
@@ -2380,7 +2517,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_with_nil_index_option_override
- output_buffer = fields_for("post[]", @post, :index => nil) do |f|
+ output_buffer = fields_for("post[]", @post, index: nil) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
@@ -2396,7 +2533,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_with_index_option_override
- output_buffer = fields_for("post[]", @post, :index => "abc") do |f|
+ output_buffer = fields_for("post[]", @post, index: "abc") do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
@@ -2455,7 +2592,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_object_with_bracketed_name_and_index
- output_buffer = fields_for("author[post]", @post, :index => 1) do |f|
+ output_buffer = fields_for("author[post]", @post, index: 1) do |f|
concat f.label(:title)
concat f.text_field(:title)
end
@@ -2470,7 +2607,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_and_fields_for
- form_for(@post, :as => :post, :html => { :id => 'create-post' }) do |post_form|
+ form_for(@post, as: :post, html: { id: 'create-post' }) do |post_form|
concat post_form.text_field(:title)
concat post_form.text_area(:body)
@@ -2479,7 +2616,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'create-post', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'create-post', 'edit_post', method: 'patch') do
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='parent_post[secret]' type='hidden' value='0' />" +
@@ -2490,7 +2627,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_and_fields_for_with_object
- form_for(@post, :as => :post, :html => { :id => 'create-post' }) do |post_form|
+ form_for(@post, as: :post, html: { id: 'create-post' }) do |post_form|
concat post_form.text_field(:title)
concat post_form.text_area(:body)
@@ -2499,7 +2636,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'create-post', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'create-post', 'edit_post', method: 'patch') do
"<input name='post[title]' type='text' id='post_title' value='Hello World' />" +
"<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" +
"<input name='post[comment][name]' type='text' id='post_comment_name' value='new comment' />"
@@ -2515,7 +2652,7 @@ class FormHelperTest < ActionView::TestCase
}
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<input name='post[category][name]' type='text' id='post_category_name' />"
end
@@ -2533,13 +2670,13 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_labelled_builder
- form_for(@post, :builder => LabelledFormBuilder) do |f|
+ form_for(@post, builder: LabelledFormBuilder) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>" +
"<label for='body'>Body:</label> <textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea><br/>" +
"<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>"
@@ -2558,7 +2695,7 @@ class FormHelperTest < ActionView::TestCase
concat f.check_box(:secret)
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>" +
"<label for='body'>Body:</label> <textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea><br/>" +
"<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>"
@@ -2577,7 +2714,7 @@ class FormHelperTest < ActionView::TestCase
concat f.text_field(:title)
end
- expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do
+ expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do
"<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>"
end
@@ -2587,7 +2724,7 @@ class FormHelperTest < ActionView::TestCase
end
def test_fields_for_with_labelled_builder
- output_buffer = fields_for(:post, @post, :builder => LabelledFormBuilder) do |f|
+ output_buffer = fields_for(:post, @post, builder: LabelledFormBuilder) do |f|
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
@@ -2604,7 +2741,7 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_labelled_builder_with_nested_fields_for_without_options_hash
klass = nil
- form_for(@post, :builder => LabelledFormBuilder) do |f|
+ form_for(@post, builder: LabelledFormBuilder) do |f|
f.fields_for(:comments, Comment.new) do |nested_fields|
klass = nested_fields.class
''
@@ -2617,8 +2754,8 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_labelled_builder_with_nested_fields_for_with_options_hash
klass = nil
- form_for(@post, :builder => LabelledFormBuilder) do |f|
- f.fields_for(:comments, Comment.new, :index => 'foo') do |nested_fields|
+ form_for(@post, builder: LabelledFormBuilder) do |f|
+ f.fields_for(:comments, Comment.new, index: 'foo') do |nested_fields|
klass = nested_fields.class
''
end
@@ -2630,7 +2767,7 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_labelled_builder_path
path = nil
- form_for(@post, :builder => LabelledFormBuilder) do |f|
+ form_for(@post, builder: LabelledFormBuilder) do |f|
path = f.to_partial_path
''
end
@@ -2643,8 +2780,8 @@ class FormHelperTest < ActionView::TestCase
def test_form_for_with_labelled_builder_with_nested_fields_for_with_custom_builder
klass = nil
- form_for(@post, :builder => LabelledFormBuilder) do |f|
- f.fields_for(:comments, Comment.new, :builder => LabelledFormBuilderSubclass) do |nested_fields|
+ form_for(@post, builder: LabelledFormBuilder) do |f|
+ f.fields_for(:comments, Comment.new, builder: LabelledFormBuilderSubclass) do |nested_fields|
klass = nested_fields.class
''
end
@@ -2654,36 +2791,36 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_html_options_adds_options_to_form_tag
- form_for(@post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end
- expected = whole_form("/posts/123", "some_form", "some_class", 'patch')
+ form_for(@post, html: { id: 'some_form', class: 'some_class' }) do |f| end
+ expected = whole_form("/posts/123", "some_form", "some_class", method: "patch")
assert_dom_equal expected, output_buffer
end
def test_form_for_with_string_url_option
- form_for(@post, :url => 'http://www.otherdomain.com') do |f| end
+ form_for(@post, url: 'http://www.otherdomain.com') do |f| end
- assert_equal whole_form("http://www.otherdomain.com", 'edit_post_123', 'edit_post', 'patch'), output_buffer
+ assert_equal whole_form("http://www.otherdomain.com", "edit_post_123", "edit_post", method: "patch"), output_buffer
end
def test_form_for_with_hash_url_option
- form_for(@post, :url => {:controller => 'controller', :action => 'action'}) do |f| end
+ form_for(@post, url: { controller: 'controller', action: 'action' }) do |f| end
assert_equal 'controller', @url_for_options[:controller]
assert_equal 'action', @url_for_options[:action]
end
def test_form_for_with_record_url_option
- form_for(@post, :url => @post) do |f| end
+ form_for(@post, url: @post) do |f| end
- expected = whole_form("/posts/123", 'edit_post_123', 'edit_post', 'patch')
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", method: "patch")
assert_equal expected, output_buffer
end
def test_form_for_with_existing_object
form_for(@post) do |f| end
- expected = whole_form("/posts/123", "edit_post_123", "edit_post", 'patch')
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", method: "patch")
assert_equal expected, output_buffer
end
@@ -2702,7 +2839,7 @@ class FormHelperTest < ActionView::TestCase
@comment.save
form_for([@post, @comment]) {}
- expected = whole_form(post_comment_path(@post, @comment), "edit_comment_1", "edit_comment", 'patch')
+ expected = whole_form(post_comment_path(@post, @comment), "edit_comment_1", "edit_comment", method: "patch")
assert_dom_equal expected, output_buffer
end
@@ -2717,7 +2854,7 @@ class FormHelperTest < ActionView::TestCase
@comment.save
form_for([:admin, @post, @comment]) {}
- expected = whole_form(admin_post_comment_path(@post, @comment), "edit_comment_1", "edit_comment", 'patch')
+ expected = whole_form(admin_post_comment_path(@post, @comment), "edit_comment_1", "edit_comment", method: "patch")
assert_dom_equal expected, output_buffer
end
@@ -2729,15 +2866,15 @@ class FormHelperTest < ActionView::TestCase
end
def test_form_for_with_existing_object_and_custom_url
- form_for(@post, :url => "/super_posts") do |f| end
+ form_for(@post, url: "/super_posts") do |f| end
- expected = whole_form("/super_posts", "edit_post_123", "edit_post", 'patch')
+ expected = whole_form("/super_posts", "edit_post_123", "edit_post", method: "patch")
assert_equal expected, output_buffer
end
def test_form_for_with_default_method_as_patch
form_for(@post) {}
- expected = whole_form("/posts/123", "edit_post_123", "edit_post", "patch")
+ expected = whole_form("/posts/123", "edit_post_123", "edit_post", method: "patch")
assert_dom_equal expected, output_buffer
end
@@ -2798,14 +2935,10 @@ class FormHelperTest < ActionView::TestCase
txt << %{ method="#{method}">}
end
- def whole_form(action = "/", id = nil, html_class = nil, options = nil)
+ def whole_form(action = "/", id = nil, html_class = nil, options = {})
contents = block_given? ? yield : ""
- if options.is_a?(Hash)
- method, remote, multipart = options.values_at(:method, :remote, :multipart)
- else
- method = options
- end
+ method, remote, multipart = options.values_at(:method, :remote, :multipart)
form_text(action, id, html_class, remote, multipart, method) + hidden_fields(method) + contents + "</form>"
end
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index 56919dc592..1eed8adb62 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -27,8 +27,8 @@ class JavaScriptHelperTest < ActionView::TestCase
assert_equal %(This \\"thing\\" is really\\n netos\\'), escape_javascript(%(This "thing" is really\n netos'))
assert_equal %(backslash\\\\test), escape_javascript( %(backslash\\test) )
assert_equal %(dont <\\/close> tags), escape_javascript(%(dont </close> tags))
- assert_equal %(unicode &#x2028; newline), escape_javascript(%(unicode \342\200\250 newline).force_encoding('UTF-8').encode!)
- assert_equal %(unicode &#x2029; newline), escape_javascript(%(unicode \342\200\251 newline).force_encoding('UTF-8').encode!)
+ assert_equal %(unicode &#x2028; newline), escape_javascript(%(unicode \342\200\250 newline).force_encoding(Encoding::UTF_8).encode!)
+ assert_equal %(unicode &#x2029; newline), escape_javascript(%(unicode \342\200\251 newline).force_encoding(Encoding::UTF_8).encode!)
assert_equal %(dont <\\/close> tags), j(%(dont </close> tags))
end
diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb
index 9c49438f6a..ab84bccb56 100644
--- a/actionpack/test/template/record_tag_helper_test.rb
+++ b/actionpack/test/template/record_tag_helper_test.rb
@@ -41,6 +41,12 @@ class RecordTagHelperTest < ActionView::TestCase
assert_dom_equal expected, actual
end
+ def test_content_tag_for_with_array_css_class
+ expected = %(<tr class="record_tag_post special odd" id="record_tag_post_45"></tr>)
+ actual = content_tag_for(:tr, @post, class: ["special", "odd"])
+ assert_dom_equal expected, actual
+ end
+
def test_content_tag_for_with_prefix_and_extra_html_options
expected = %(<tr class="archived_record_tag_post special" id="archived_record_tag_post_45" style='background-color: #f0f0f0'></tr>)
actual = content_tag_for(:tr, @post, :archived, class: "special", style: "background-color: #f0f0f0")
diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb
index c7231d9cd5..acd002ce73 100644
--- a/actionpack/test/template/test_case_test.rb
+++ b/actionpack/test/template/test_case_test.rb
@@ -329,6 +329,27 @@ module ActionView
assert_template partial: '_partial', locals: { 'second' => '2' }
end
+ test 'raises descriptive error message when template was not rendered' do
+ controller.controller_path = "test"
+ render(template: "test/hello_world_with_partial")
+ e = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_template partial: 'i_was_never_rendered', locals: { 'did_not' => 'happen' }
+ end
+ assert_match "i_was_never_rendered to be rendered but it was not.", e.message
+ assert_match 'Expected ["/test/partial"] to include "i_was_never_rendered"', e.message
+ end
+
+ test 'specifying locals works when the partial is inside a directory with underline prefix' do
+ controller.controller_path = "test"
+ render(template: 'test/render_partial_inside_directory')
+ assert_template partial: 'test/_directory/_partial_with_locales', locals: { 'name' => 'Jane' }
+ end
+
+ test 'specifying locals works when the partial is inside a directory without underline prefix' do
+ controller.controller_path = "test"
+ render(template: 'test/render_partial_inside_directory')
+ assert_template partial: 'test/_directory/partial_with_locales', locals: { 'name' => 'Jane' }
+ end
end
module AHelperWithInitialize
diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb
index 412d13bb2b..1b2234f4e2 100644
--- a/actionpack/test/template/text_helper_test.rb
+++ b/actionpack/test/template/text_helper_test.rb
@@ -95,8 +95,8 @@ class TextHelperTest < ActionView::TestCase
end
def test_truncate_multibyte
- assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'),
- truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), :length => 10)
+ assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding(Encoding::UTF_8),
+ truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding(Encoding::UTF_8), :length => 10)
end
def test_truncate_does_not_modify_the_options_hash
@@ -293,7 +293,7 @@ class TextHelperTest < ActionView::TestCase
end
def test_excerpt_with_utf8
- assert_equal("...\357\254\203ciency could not be...".force_encoding('UTF-8'), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding('UTF-8'), 'could', :radius => 8))
+ assert_equal("...\357\254\203ciency could not be...".force_encoding(Encoding::UTF_8), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding(Encoding::UTF_8), 'could', :radius => 8))
end
def test_excerpt_does_not_modify_the_options_hash
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index ba65349b6a..5d87c96605 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -437,6 +437,12 @@ class UrlHelperTest < ActiveSupport::TestCase
ActionDispatch::Request.new(env)
end
+ def test_current_page_with_http_head_method
+ @request = request_for_url("/", :method => :head)
+ assert current_page?(url_hash)
+ assert current_page?("http://www.example.com/")
+ end
+
def test_current_page_with_simple_url
@request = request_for_url("/")
assert current_page?(url_hash)
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index c52e4947ae..b5562dda2e 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/extract_options'
+
module ActiveModel
# == Active \Model \Callbacks
#
diff --git a/activemodel/test/cases/naming_test.rb b/activemodel/test/cases/naming_test.rb
index 49d8706ac2..38ba3cc152 100644
--- a/activemodel/test/cases/naming_test.rb
+++ b/activemodel/test/cases/naming_test.rb
@@ -29,6 +29,14 @@ class NamingTest < ActiveModel::TestCase
assert_equal 'Track back', @model_name.human
end
+ def test_route_key
+ assert_equal 'post_track_backs', @model_name.route_key
+ end
+
+ def test_param_key
+ assert_equal 'post_track_back', @model_name.param_key
+ end
+
def test_i18n_key
assert_equal :"post/track_back", @model_name.i18n_key
end
diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb
index 1a40ca8efc..8b2f886cc4 100644
--- a/activemodel/test/cases/validations/length_validation_test.rb
+++ b/activemodel/test/cases/validations/length_validation_test.rb
@@ -5,13 +5,12 @@ require 'models/topic'
require 'models/person'
class LengthValidationTest < ActiveModel::TestCase
-
def teardown
Topic.reset_callbacks(:validate)
end
def test_validates_length_of_with_allow_nil
- Topic.validates_length_of( :title, :is => 5, :allow_nil => true )
+ Topic.validates_length_of( :title, is: 5, allow_nil: true )
assert Topic.new("title" => "ab").invalid?
assert Topic.new("title" => "").invalid?
@@ -20,7 +19,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_with_allow_blank
- Topic.validates_length_of( :title, :is => 5, :allow_blank => true )
+ Topic.validates_length_of( :title, is: 5, allow_blank: true )
assert Topic.new("title" => "ab").invalid?
assert Topic.new("title" => "").valid?
@@ -29,7 +28,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_minimum
- Topic.validates_length_of :title, :minimum => 5
+ Topic.validates_length_of :title, minimum: 5
t = Topic.new("title" => "valid", "content" => "whatever")
assert t.valid?
@@ -51,13 +50,13 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_maximum_should_allow_nil
- Topic.validates_length_of :title, :maximum => 10
+ Topic.validates_length_of :title, maximum: 10
t = Topic.new
assert t.valid?
end
def test_optionally_validates_length_of_using_minimum
- Topic.validates_length_of :title, :minimum => 5, :allow_nil => true
+ Topic.validates_length_of :title, minimum: 5, allow_nil: true
t = Topic.new("title" => "valid", "content" => "whatever")
assert t.valid?
@@ -67,7 +66,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_maximum
- Topic.validates_length_of :title, :maximum => 5
+ Topic.validates_length_of :title, maximum: 5
t = Topic.new("title" => "valid", "content" => "whatever")
assert t.valid?
@@ -82,7 +81,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_optionally_validates_length_of_using_maximum
- Topic.validates_length_of :title, :maximum => 5, :allow_nil => true
+ Topic.validates_length_of :title, maximum: 5, allow_nil: true
t = Topic.new("title" => "valid", "content" => "whatever")
assert t.valid?
@@ -92,7 +91,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_within
- Topic.validates_length_of(:title, :content, :within => 3..5)
+ Topic.validates_length_of(:title, :content, within: 3..5)
t = Topic.new("title" => "a!", "content" => "I'm ooooooooh so very long")
assert t.invalid?
@@ -111,7 +110,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_within_with_exclusive_range
- Topic.validates_length_of(:title, :within => 4...10)
+ Topic.validates_length_of(:title, within: 4...10)
t = Topic.new("title" => "9 chars!!")
assert t.valid?
@@ -125,7 +124,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_optionally_validates_length_of_using_within
- Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
+ Topic.validates_length_of :title, :content, within: 3..5, allow_nil: true
t = Topic.new('title' => 'abc', 'content' => 'abcd')
assert t.valid?
@@ -135,7 +134,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_is
- Topic.validates_length_of :title, :is => 5
+ Topic.validates_length_of :title, is: 5
t = Topic.new("title" => "valid", "content" => "whatever")
assert t.valid?
@@ -153,7 +152,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_optionally_validates_length_of_using_is
- Topic.validates_length_of :title, :is => 5, :allow_nil => true
+ Topic.validates_length_of :title, is: 5, allow_nil: true
t = Topic.new("title" => "valid", "content" => "whatever")
assert t.valid?
@@ -167,25 +166,25 @@ class LengthValidationTest < ActiveModel::TestCase
bigmax = 2 ** 32
bigrange = bigmin...bigmax
assert_nothing_raised do
- Topic.validates_length_of :title, :is => bigmin + 5
- Topic.validates_length_of :title, :within => bigrange
- Topic.validates_length_of :title, :in => bigrange
- Topic.validates_length_of :title, :minimum => bigmin
- Topic.validates_length_of :title, :maximum => bigmax
+ Topic.validates_length_of :title, is: bigmin + 5
+ Topic.validates_length_of :title, within: bigrange
+ Topic.validates_length_of :title, in: bigrange
+ Topic.validates_length_of :title, minimum: bigmin
+ Topic.validates_length_of :title, maximum: bigmax
end
end
def test_validates_length_of_nasty_params
- assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is => -6) }
- assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within => 6) }
- assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum => "a") }
- assert_raise(ArgumentError) { Topic.validates_length_of(:title, :maximum => "a") }
- assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within => "a") }
- assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is => "a") }
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, is: -6) }
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, within: 6) }
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, minimum: "a") }
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, maximum: "a") }
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, within: "a") }
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, is: "a") }
end
def test_validates_length_of_custom_errors_for_minimum_with_message
- Topic.validates_length_of( :title, :minimum => 5, :message => "boo %{count}" )
+ Topic.validates_length_of( :title, minimum: 5, message: "boo %{count}" )
t = Topic.new("title" => "uhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -193,7 +192,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_minimum_with_too_short
- Topic.validates_length_of( :title, :minimum => 5, :too_short => "hoo %{count}" )
+ Topic.validates_length_of( :title, minimum: 5, too_short: "hoo %{count}" )
t = Topic.new("title" => "uhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -201,7 +200,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_maximum_with_message
- Topic.validates_length_of( :title, :maximum => 5, :message => "boo %{count}" )
+ Topic.validates_length_of( :title, maximum: 5, message: "boo %{count}" )
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -209,7 +208,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_in
- Topic.validates_length_of(:title, :in => 10..20, :message => "hoo %{count}")
+ Topic.validates_length_of(:title, in: 10..20, message: "hoo %{count}")
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -222,7 +221,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_maximum_with_too_long
- Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}" )
+ Topic.validates_length_of( :title, maximum: 5, too_long: "hoo %{count}" )
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -230,21 +229,21 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_both_too_short_and_too_long
- Topic.validates_length_of :title, :minimum => 3, :maximum => 5, :too_short => 'too short', :too_long => 'too long'
+ Topic.validates_length_of :title, minimum: 3, maximum: 5, too_short: 'too short', too_long: 'too long'
- t = Topic.new(:title => 'a')
+ t = Topic.new(title: 'a')
assert t.invalid?
assert t.errors[:title].any?
assert_equal ['too short'], t.errors['title']
- t = Topic.new(:title => 'aaaaaa')
+ t = Topic.new(title: 'aaaaaa')
assert t.invalid?
assert t.errors[:title].any?
assert_equal ['too long'], t.errors['title']
end
def test_validates_length_of_custom_errors_for_is_with_message
- Topic.validates_length_of( :title, :is => 5, :message => "boo %{count}" )
+ Topic.validates_length_of( :title, is: 5, message: "boo %{count}" )
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -252,7 +251,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_custom_errors_for_is_with_wrong_length
- Topic.validates_length_of( :title, :is => 5, :wrong_length => "hoo %{count}" )
+ Topic.validates_length_of( :title, is: 5, wrong_length: "hoo %{count}" )
t = Topic.new("title" => "uhohuhoh", "content" => "whatever")
assert t.invalid?
assert t.errors[:title].any?
@@ -260,7 +259,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_minimum_utf8
- Topic.validates_length_of :title, :minimum => 5
+ Topic.validates_length_of :title, minimum: 5
t = Topic.new("title" => "一二三四五", "content" => "whatever")
assert t.valid?
@@ -272,7 +271,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_maximum_utf8
- Topic.validates_length_of :title, :maximum => 5
+ Topic.validates_length_of :title, maximum: 5
t = Topic.new("title" => "一二三四五", "content" => "whatever")
assert t.valid?
@@ -284,7 +283,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_within_utf8
- Topic.validates_length_of(:title, :content, :within => 3..5)
+ Topic.validates_length_of(:title, :content, within: 3..5)
t = Topic.new("title" => "一二", "content" => "12三四五六七")
assert t.invalid?
@@ -296,12 +295,12 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_optionally_validates_length_of_using_within_utf8
- Topic.validates_length_of :title, :within => 3..5, :allow_nil => true
+ Topic.validates_length_of :title, within: 3..5, allow_nil: true
- t = Topic.new(:title => "一二三四五")
+ t = Topic.new(title: "一二三四五")
assert t.valid?, t.errors.inspect
- t = Topic.new(:title => "一二三")
+ t = Topic.new(title: "一二三")
assert t.valid?, t.errors.inspect
t.title = nil
@@ -309,7 +308,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_is_utf8
- Topic.validates_length_of :title, :is => 5
+ Topic.validates_length_of :title, is: 5
t = Topic.new("title" => "一二345", "content" => "whatever")
assert t.valid?
@@ -321,9 +320,9 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_with_block
- Topic.validates_length_of :content, :minimum => 5, :too_short => "Your essay must be at least %{count} words.",
- :tokenizer => lambda {|str| str.scan(/\w+/) }
- t = Topic.new(:content => "this content should be long enough")
+ Topic.validates_length_of :content, minimum: 5, too_short: "Your essay must be at least %{count} words.",
+ tokenizer: lambda {|str| str.scan(/\w+/) }
+ t = Topic.new(content: "this content should be long enough")
assert t.valid?
t.content = "not long enough"
@@ -333,18 +332,18 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_for_fixnum
- Topic.validates_length_of(:approved, :is => 4)
+ Topic.validates_length_of(:approved, is: 4)
- t = Topic.new("title" => "uhohuhoh", "content" => "whatever", :approved => 1)
+ t = Topic.new("title" => "uhohuhoh", "content" => "whatever", approved: 1)
assert t.invalid?
assert t.errors[:approved].any?
- t = Topic.new("title" => "uhohuhoh", "content" => "whatever", :approved => 1234)
+ t = Topic.new("title" => "uhohuhoh", "content" => "whatever", approved: 1234)
assert t.valid?
end
def test_validates_length_of_for_ruby_class
- Person.validates_length_of :karma, :minimum => 5
+ Person.validates_length_of :karma, minimum: 5
p = Person.new
p.karma = "Pix"
@@ -359,7 +358,7 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_for_infinite_maxima
- Topic.validates_length_of(:title, :within => 5..Float::INFINITY)
+ Topic.validates_length_of(:title, within: 5..Float::INFINITY)
t = Topic.new("title" => "1234")
assert t.invalid?
@@ -368,7 +367,7 @@ class LengthValidationTest < ActiveModel::TestCase
t.title = "12345"
assert t.valid?
- Topic.validates_length_of(:author_name, :maximum => Float::INFINITY)
+ Topic.validates_length_of(:author_name, maximum: Float::INFINITY)
assert t.valid?
@@ -377,13 +376,13 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_maximum_should_not_allow_nil_when_nil_not_allowed
- Topic.validates_length_of :title, :maximum => 10, :allow_nil => false
+ Topic.validates_length_of :title, maximum: 10, allow_nil: false
t = Topic.new
assert t.invalid?
end
def test_validates_length_of_using_maximum_should_not_allow_nil_and_empty_string_when_blank_not_allowed
- Topic.validates_length_of :title, :maximum => 10, :allow_blank => false
+ Topic.validates_length_of :title, maximum: 10, allow_blank: false
t = Topic.new
assert t.invalid?
@@ -392,13 +391,13 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_both_minimum_and_maximum_should_not_allow_nil
- Topic.validates_length_of :title, :minimum => 5, :maximum => 10
+ Topic.validates_length_of :title, minimum: 5, maximum: 10
t = Topic.new
assert t.invalid?
end
def test_validates_length_of_using_minimum_0_should_not_allow_nil
- Topic.validates_length_of :title, :minimum => 0
+ Topic.validates_length_of :title, minimum: 0
t = Topic.new
assert t.invalid?
@@ -407,11 +406,19 @@ class LengthValidationTest < ActiveModel::TestCase
end
def test_validates_length_of_using_is_0_should_not_allow_nil
- Topic.validates_length_of :title, :is => 0
+ Topic.validates_length_of :title, is: 0
t = Topic.new
assert t.invalid?
t.title = ""
assert t.valid?
end
+
+ def test_validates_with_diff_in_option
+ Topic.validates_length_of(:title, is: 5)
+ Topic.validates_length_of(:title, is: 5, if: Proc.new { false } )
+
+ assert Topic.new("title" => "david").valid?
+ assert Topic.new("title" => "david2").invalid?
+ end
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 5a0c391154..4489ca1aff 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,169 @@
## Rails 4.0.0 (unreleased) ##
+* Preloading `has_many :through` associations with conditions won't
+ cache the `:through` association. This will prevent invalid
+ subsets to be cached.
+ Fixes #8423.
+
+ Example:
+
+ class User
+ has_many :posts
+ has_many :recent_comments, -> { where('created_at > ?', 1.week.ago) }, :through => :posts
+ end
+
+ a_user = User.includes(:recent_comments).first
+
+ # this is preloaded
+ a_user.recent_comments
+
+ # fetching the recent_comments through the posts association won't preload it.
+ a_user.posts
+
+ *Yves Senn*
+
+* Don't run after_commit callback when creating through an association
+ if saving the record fails.
+
+ *James Miller *
+
+* Allow store accessors to be overrided like other attribute methods, e.g.:
+
+ class User < ActiveRecord::Base
+ store :settings, accessors: [ :color, :homepage ], coder: JSON
+
+ def color
+ super || 'red'
+ end
+ end
+
+ *Sergey Nartimov*
+
+* Quote numeric values being compared to non-numeric columns. Otherwise,
+ in some database, the string column values will be coerced to a numeric
+ allowing 0, 0.0 or false to match any string starting with a non-digit.
+
+ Example:
+
+ App.where(apikey: 0) # => SELECT * FROM users WHERE apikey = '0'
+
+ *Dylan Smith*
+
+* Schema dumper supports dumping the enabled database extensions to `schema.rb`
+ (currently only supported by postgresql).
+
+ *Justin George*
+
+* The `DATABASE_URL` environment variable now converts ints, floats, and
+ the strings true and false to Ruby types. For example, SQLite requires
+ that the timeout value is an integer, and PostgreSQL requires that the
+ prepared_statements option is a boolean. These now work as expected:
+
+ Example:
+
+ DATABASE_URL=sqlite3://localhost/test_db?timeout=500
+ DATABASE_URL=postgresql://localhost/test_db?prepared_statements=false
+
+ *Aaron Stone*
+
+* `Relation#merge` now only overwrites where values on the LHS of the
+ merge. Consider:
+
+ left = Person.where(age: [13, 14, 15])
+ right = Person.where(age: [13, 14]).where(age: [14, 15])
+
+ `left` results in the following SQL:
+
+ WHERE age IN (13, 14, 15)
+
+ `right` results in the following SQL:
+
+ WHERE age IN (13, 14) AND age IN (14, 15)
+
+ Previously, `left.merge(right)` would result in all but the last
+ condition being removed:
+
+ WHERE age IN (14, 15)
+
+ Now it results in the LHS condition(s) for `age` being removed, but
+ the RHS remains as it is:
+
+ WHERE age IN (13, 14) AND age IN (14, 15)
+
+ *Jon Leighton*
+
+* Fix handling of dirty time zone aware attributes
+
+ Previously, when `time_zone_aware_attributes` were enabled, after
+ changing a datetime or timestamp attribute and then changing it back
+ to the original value, `changed_attributes` still tracked the
+ attribute as changed. This caused `[attribute]_changed?` and
+ `changed?` methods to return true incorrectly.
+
+ Example:
+
+ in_time_zone 'Paris' do
+ order = Order.new
+ original_time = Time.local(2012, 10, 10)
+ order.shipped_at = original_time
+ order.save
+ order.changed? # => false
+
+ # changing value
+ order.shipped_at = Time.local(2013, 1, 1)
+ order.changed? # => true
+
+ # reverting to original value
+ order.shipped_at = original_time
+ order.changed? # => false, used to return true
+ end
+
+ *Lilibeth De La Cruz*
+
+* When `#count` is used in conjunction with `#uniq` we perform `count(:distinct => true)`.
+ Fix #6865.
+
+ Example:
+
+ relation.uniq.count # => SELECT COUNT(DISTINCT *)
+
+ *Yves Senn + Kaspar Schiess*
+
+* PostgreSQL ranges type support. Includes: int4range, int8range,
+ numrange, tsrange, tstzrange, daterange
+
+ Ranges can be created with inclusive and exclusive bounds.
+
+ Example:
+
+ create_table :Room do |t|
+ t.daterange :availability
+ end
+
+ Room.create(availability: (Date.today..Float::INFINITY))
+ Room.first.availability # => Wed, 19 Sep 2012..Infinity
+
+ One thing to note: Range class does not support exclusive lower
+ bound.
+
+ *Alexander Grebennik*
+
+* Added a state instance variable to each transaction. Will allow other objects
+ to know whether a transaction has been committed or rolled back.
+
+ *John Wang*
+
+* Collection associations `#empty?` always respects builded records.
+ Fix #8879.
+
+ Example:
+
+ widget = Widget.new
+ widget.things.build
+ widget.things.empty? # => false
+
+ *Yves Senn*
+
* Remove support for parsing YAML parameters from request.
*Aaron Patterson*
@@ -481,7 +645,7 @@
After:
- #=> SELECT * FROM users WHERE 1 = 2;
+ #=> SELECT * FROM users WHERE 1=0;
*Damien Mathieu*
@@ -503,7 +667,7 @@
*Matt Jones*
-* Accept belongs_to (including polymorphic) association keys in queries.
+* Accept `belongs_to` (including polymorphic) association keys in queries.
The following queries are now equivalent:
@@ -974,7 +1138,6 @@
* `:conditions` becomes `:where`.
* `:include` becomes `:includes`.
- * `:extend` becomes `:extending`.
The code to implement the deprecated features has been moved out to
the `activerecord-deprecated_finders` gem. This gem is a dependency
diff --git a/activerecord/RUNNING_UNIT_TESTS b/activerecord/RUNNING_UNIT_TESTS.rdoc
index bdd8834dcb..bdd8834dcb 100644
--- a/activerecord/RUNNING_UNIT_TESTS
+++ b/activerecord/RUNNING_UNIT_TESTS.rdoc
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index 53ddff420e..0523314128 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -39,6 +39,11 @@ namespace :test do
end
end
+namespace :db do
+ task :create => ['mysql:build_databases', 'postgresql:build_databases']
+ task :drop => ['mysql:drop_databases', 'postgresql:drop_databases']
+end
+
%w( mysql mysql2 postgresql sqlite3 sqlite3_mem firebird db2 oracle sybase openbase frontbase jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb ).each do |adapter|
Rake::TestTask.new("test_#{adapter}") { |t|
adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z0-9]+/]
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec
index 31ddb01123..d523c1eca1 100644
--- a/activerecord/activerecord.gemspec
+++ b/activerecord/activerecord.gemspec
@@ -25,5 +25,5 @@ Gem::Specification.new do |s|
s.add_dependency 'activemodel', version
s.add_dependency 'arel', '~> 3.0.2'
- s.add_dependency 'activerecord-deprecated_finders', '0.0.1'
+ s.add_dependency 'activerecord-deprecated_finders', '~> 0.0.3'
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 16a46a59d1..06bdabfced 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -191,7 +191,7 @@ module ActiveRecord
# * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
# * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
# * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
- # <tt>Project#milestones.delete(milestone), Project#milestones.destroy(mileston), Project#milestones.find(milestone_id),</tt>
+ # <tt>Project#milestones.delete(milestone), Project#milestones.destroy(milestone), Project#milestones.find(milestone_id),</tt>
# <tt>Project#milestones.build, Project#milestones.create</tt>
# * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
# <tt>Project#categories.delete(category1), Project#categories.destroy(category1)</tt>
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 1303822868..c5fb1fe2c7 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -15,7 +15,7 @@ module ActiveRecord
def scope
scope = klass.unscoped
- scope.merge! eval_scope(klass, reflection.scope) if reflection.scope
+ scope.extending! Array(options[:extend])
add_constraints(scope)
end
@@ -58,7 +58,7 @@ module ActiveRecord
if reflection.source_macro == :belongs_to
if reflection.options[:polymorphic]
- key = reflection.association_primary_key(klass)
+ key = reflection.association_primary_key(self.klass)
else
key = reflection.association_primary_key
end
@@ -91,8 +91,13 @@ module ActiveRecord
# Exclude the scope of the association itself, because that
# was already merged in the #scope method.
- (scope_chain[i] - [self.reflection.scope]).each do |scope_chain_item|
- item = eval_scope(reflection.klass, scope_chain_item)
+ scope_chain[i].each do |scope_chain_item|
+ klass = i == 0 ? self.klass : reflection.klass
+ item = eval_scope(klass, scope_chain_item)
+
+ if scope_chain_item == self.reflection.scope
+ scope.merge! item.except(:where, :includes)
+ end
scope.includes! item.includes_values
scope.where_values += item.where_values
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index fcdfc1e150..fdead16761 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -6,7 +6,8 @@ module ActiveRecord::Associations::Builder
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
def valid_options
- super + [:table_name, :finder_sql, :counter_sql, :before_add, :after_add, :before_remove, :after_remove]
+ super + [:table_name, :finder_sql, :counter_sql, :before_add,
+ :after_add, :before_remove, :after_remove, :extend]
end
attr_reader :block_extension, :extension_module
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 832b963052..5feb149946 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -273,7 +273,7 @@ module ActiveRecord
if loaded? || options[:counter_sql]
size.zero?
else
- !scope.exists?
+ @target.blank? && !scope.exists?
end
end
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 33dce58982..e93e700c93 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -33,6 +33,7 @@ module ActiveRecord
def initialize(klass, association) #:nodoc:
@association = association
super klass, klass.arel_table
+ self.default_scoped = true
merge! association.scope(nullify: false)
end
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index cd366ac8b7..f40368cfeb 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -68,7 +68,7 @@ module ActiveRecord
remove_duplicate_results!(base, records, association)
end
when Hash
- associations.keys.each do |name|
+ associations.each_key do |name|
reflection = base.reflections[name]
remove_uniq_by_reflection(reflection, records)
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index 1c1ba11c44..b0b1c13b0d 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -31,7 +31,8 @@ module ActiveRecord
through_records = Array.wrap(owner.send(through_reflection.name))
# Dont cache the association - we would only be caching a subset
- if reflection.options[:source_type] && through_reflection.collection?
+ if (through_scope != through_reflection.klass.unscoped) ||
+ (reflection.options[:source_type] && through_reflection.collection?)
owner.association(through_reflection.name).reset
end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index 47a8b576c0..41b5a6e926 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -33,20 +33,12 @@ module ActiveRecord
def define_method_attribute=(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
method_body, line = <<-EOV, __LINE__ + 1
- def #{attr_name}=(original_time)
- original_time = nil if original_time.blank?
- time = original_time
- unless time.acts_like?(:time)
- time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
- end
- zoned_time = time && time.in_time_zone rescue nil
- rounded_time = round_usec(zoned_time)
- rounded_value = round_usec(read_attribute("#{attr_name}"))
- if (rounded_value != rounded_time) || (!rounded_value && original_time)
- write_attribute("#{attr_name}", original_time)
- #{attr_name}_will_change!
- @attributes_cache["#{attr_name}"] = zoned_time
- end
+ def #{attr_name}=(time)
+ time_with_zone = time.respond_to?(:in_time_zone) ? time.in_time_zone : nil
+ previous_time = attribute_changed?("#{attr_name}") ? changed_attributes["#{attr_name}"] : read_attribute(:#{attr_name})
+ write_attribute(:#{attr_name}, time)
+ #{attr_name}_will_change! if previous_time != time_with_zone
+ @attributes_cache["#{attr_name}"] = time_with_zone
end
EOV
generated_attribute_methods.module_eval(method_body, __FILE__, line)
@@ -62,11 +54,6 @@ module ActiveRecord
[:datetime, :timestamp].include?(column.type)
end
end
-
- private
- def round_usec(value)
- value.change(usec: 0) if value
- end
end
end
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index bf5793d454..e262401da6 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -162,8 +162,8 @@ module ActiveRecord #:nodoc:
#
# Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects
# by simple queries without turning to SQL. They work by appending the name of an attribute
- # to <tt>find_by_</tt> # like <tt>Person.find_by_user_name</tt>.
- # Instead of writing # <tt>Person.where(user_name: user_name).first</tt>, you just do
+ # to <tt>find_by_</tt> like <tt>Person.find_by_user_name</tt>.
+ # Instead of writing <tt>Person.where(user_name: user_name).first</tt>, you just do
# <tt>Person.find_by_user_name(user_name)</tt>.
#
# It's possible to add an exclamation point (!) on the end of the dynamic finders to get them to raise an
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 27e6e8898c..847d9da6e6 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -237,7 +237,7 @@ module ActiveRecord
@spec = spec
@checkout_timeout = spec.config[:checkout_timeout] || 5
- @dead_connection_timeout = spec.config[:dead_connection_timeout]
+ @dead_connection_timeout = spec.config[:dead_connection_timeout] || 5
@reaper = Reaper.new self, spec.config[:reaping_frequency]
@reaper.run
@@ -517,6 +517,7 @@ module ActiveRecord
def establish_connection(owner, spec)
@class_to_pool.clear
+ raise RuntimeError, "Anonymous class is not allowed." unless owner.name
owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec)
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 be6fda95b4..41e07fbda9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -9,9 +9,9 @@ module ActiveRecord
def dirties_query_cache(base, *method_names)
method_names.each do |method_name|
base.class_eval <<-end_code, __FILE__, __LINE__ + 1
- def #{method_name}(*) # def update_with_query_dirty(*args)
+ def #{method_name}(*) # def update_with_query_dirty(*)
clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
- super # update_without_query_dirty(*args)
+ super # super
end # end
end_code
end
@@ -85,6 +85,8 @@ module ActiveRecord
end
end
+ # If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
+ # queries should not be cached.
def locked?(arel)
arel.respond_to?(:locked) && arel.locked
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 60a9eee7c7..aec4654eee 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -25,13 +25,19 @@ module ActiveRecord
when true, false
if column && column.type == :integer
value ? '1' : '0'
+ elsif column && [:text, :string, :binary].include?(column.type)
+ value ? "'1'" : "'0'"
else
value ? quoted_true : quoted_false
end
# BigDecimals need to be put in a non-normalized form and quoted.
when nil then "NULL"
- when BigDecimal then value.to_s('F')
- when Numeric, ActiveSupport::Duration then value.to_s
+ when Numeric, ActiveSupport::Duration
+ value = BigDecimal === value ? value.to_s('F') : value.to_s
+ if column && ![:integer, :float, :decimal].include?(column.type)
+ value = "'#{value}'"
+ end
+ value
when Date, Time then "'#{quoted_date(value)}'"
when Symbol then "'#{quote_string(value.to_s)}'"
when Class then "'#{value.to_s}'"
@@ -93,6 +99,18 @@ module ActiveRecord
quote_column_name(table_name)
end
+ # Override to return the quoted table name for assignment. Defaults to
+ # table quoting.
+ #
+ # This works for mysql and mysql2 where table.column can be used to
+ # resolve ambiguity.
+ #
+ # We override this in the sqlite and postgresql adapters to use only
+ # the column name (as per syntax requirements).
+ def quote_table_name_for_assignment(table, attr)
+ quote_table_name("#{table}.#{attr}")
+ end
+
def quoted_true
"'t'"
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 b1ec33d06c..f758e19a4f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -252,15 +252,12 @@ module ActiveRecord
self
end
- %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
- class_eval <<-EOV, __FILE__, __LINE__ + 1
- def #{column_type}(*args) # def string(*args)
- options = args.extract_options! # options = args.extract_options!
- column_names = args # column_names = args
- type = :'#{column_type}' # type = :string
- column_names.each { |name| column(name, type, options) } # column_names.each { |name| column(name, type, options) }
- end # end
- EOV
+ [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
+ define_method column_type do |*args|
+ options = args.extract_options!
+ column_names = args
+ column_names.each { |name| column(name, column_type, options) }
+ end
end
# Adds index options to the indexes hash, keyed by column name
@@ -486,15 +483,13 @@ module ActiveRecord
#
# t.string(:goat)
# t.string(:goat, :sheep)
- %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
- class_eval <<-EOV, __FILE__, __LINE__ + 1
- def #{column_type}(*args) # def string(*args)
- options = args.extract_options! # options = args.extract_options!
- args.each do |name| # column_names.each do |name|
- @base.add_column(@table_name, name, :#{column_type}, options) # @base.add_column(@table_name, name, :string, options)
- end # end
- end # end
- EOV
+ [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
+ define_method column_type do |*args|
+ options = args.extract_options!
+ args.each do |name|
+ @base.add_column(@table_name, name, column_type, options)
+ end
+ end
end
private
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index 9d6111b51e..fd5eaab9c9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -47,6 +47,9 @@ module ActiveRecord
value.to_s
when Date, DateTime, Time
"'#{value.to_s(:db)}'"
+ when Range
+ # infinity dumps as Infinity, which causes uninitialized constant error
+ value.inspect.gsub('Infinity', '::Float::INFINITY')
else
value.inspect
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index 4cca94e40b..3ecef96b10 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -5,6 +5,35 @@ module ActiveRecord
def initialize(connection)
@connection = connection
+ @state = TransactionState.new
+ end
+
+ def state
+ @state
+ end
+ end
+
+ class TransactionState
+
+ VALID_STATES = Set.new([:committed, :rolledback, nil])
+
+ def initialize(state = nil)
+ @state = state
+ end
+
+ def committed?
+ @state == :committed
+ end
+
+ def rolledback?
+ @state == :rolledback
+ end
+
+ def set_state(state)
+ if !VALID_STATES.include?(state)
+ raise ArgumentError, "Invalid transaction state: #{state}"
+ end
+ @state = state
end
end
@@ -91,6 +120,7 @@ module ActiveRecord
end
def rollback_records
+ @state.set_state(:rolledback)
records.uniq.each do |record|
begin
record.rolledback!(parent.closed?)
@@ -101,6 +131,7 @@ module ActiveRecord
end
def commit_records
+ @state.set_state(:committed)
records.uniq.each do |record|
begin
record.committed!
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index cbb6869e66..2400fc92cc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -171,10 +171,22 @@ module ActiveRecord
false
end
+ # Does this adapter support database extensions? As of this writing
+ # only postgresql does.
+ def supports_extensions?
+ false
+ end
+
+ # A list of extensions, to be filled in by databases that
+ # support them (at the moment, postgresql).
+ def extensions
+ []
+ end
+
# QUOTING ==================================================
# Returns a bind substitution value given a +column+ and list of current
- # +binds+
+ # +binds+.
def substitute_at(column, index)
Arel::Nodes::BindParam.new '?'
end
@@ -293,6 +305,10 @@ module ActiveRecord
pool.checkin self
end
+ def valid_type?(type)
+ true
+ end
+
protected
def log(sql, name = "SQL", binds = [])
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 52b0b3fe79..e3ad5594d2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -212,8 +212,6 @@ module ActiveRecord
if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
s = column.class.string_to_binary(value).unpack("H*")[0]
"x'#{s}'"
- elsif value.kind_of?(BigDecimal)
- value.to_s("F")
else
super
end
@@ -584,6 +582,10 @@ module ActiveRecord
@config.fetch(:strict, true)
end
+ def valid_type?(type)
+ !native_database_types[type].nil?
+ end
+
protected
# MySQL is too stupid to create a temporary table for use subquery, so we have
@@ -749,7 +751,6 @@ module ActiveRecord
# ...and send them all in one query
execute("SET #{encoding} #{variable_assignments}", :skip_logging)
end
-
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index fb28ecb6cf..a4b3a0c584 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -74,12 +74,13 @@ module ActiveRecord
def type_cast_for_write(value)
return value unless number?
- if value == false
+ case value
+ when FalseClass
0
- elsif value == true
+ when TrueClass
1
- elsif value.is_a?(String) && value.blank?
- nil
+ when String
+ value.presence
else
value
end
@@ -126,7 +127,6 @@ module ActiveRecord
when :hstore then "#{klass}.string_to_hstore(#{var_name})"
when :inet, :cidr then "#{klass}.string_to_cidr(#{var_name})"
when :json then "#{klass}.string_to_json(#{var_name})"
- when :intrange then "#{klass}.string_to_intrange(#{var_name})"
else var_name
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 09250d3c01..577a362568 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -62,6 +62,10 @@ module ActiveRecord
ConnectionSpecification.new(spec, adapter_method)
end
+ # For DATABASE_URL, accept a limited concept of ints and floats
+ SIMPLE_INT = /\A\d+\z/
+ SIMPLE_FLOAT = /\A\d+\.\d+\z/
+
def connection_url_to_hash(url) # :nodoc:
config = URI.parse url
adapter = config.scheme
@@ -72,15 +76,38 @@ module ActiveRecord
:port => config.port,
:database => config.path.sub(%r{^/},""),
:host => config.host }
+
spec.reject!{ |_,value| value.blank? }
+
uri_parser = URI::Parser.new
+
spec.map { |key,value| spec[key] = uri_parser.unescape(value) if value.is_a?(String) }
+
if config.query
options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
+
+ options.each { |key, value| options[key] = type_cast_value(value) }
+
spec.merge!(options)
end
+
spec
end
+
+ def type_cast_value(value)
+ case value
+ when SIMPLE_INT
+ value.to_i
+ when SIMPLE_FLOAT
+ value.to_f
+ when 'true'
+ true
+ when 'false'
+ false
+ else
+ value
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
index f7d734a2f1..3d8f0b575c 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -62,6 +62,12 @@ module ActiveRecord
"{#{casted_values.join(',')}}"
end
+ def range_to_string(object)
+ from = object.begin.respond_to?(:infinite?) && object.begin.infinite? ? '' : object.begin
+ to = object.end.respond_to?(:infinite?) && object.end.infinite? ? '' : object.end
+ "[#{from},#{to}#{object.exclude_end? ? ')' : ']'}"
+ end
+
def string_to_json(string)
if String === string
ActiveSupport::JSON.decode(string)
@@ -92,36 +98,6 @@ module ActiveRecord
parse_pg_array(string).map{|val| oid.type_cast val}
end
- def string_to_intrange(string)
- if string.nil?
- nil
- elsif "empty" == string
- (nil..nil)
- elsif String === string && (matches = /^(\(|\[)([0-9]+),(\s?)([0-9]+)(\)|\])$/i.match(string))
- lower_bound = ("(" == matches[1] ? (matches[2].to_i + 1) : matches[2].to_i)
- upper_bound = (")" == matches[5] ? (matches[4].to_i - 1) : matches[4].to_i)
- (lower_bound..upper_bound)
- else
- string
- end
- end
-
- def intrange_to_string(object)
- if object.nil?
- nil
- elsif Range === object
- if [object.first, object.last].all? { |el| Integer === el }
- "[#{object.first.to_i},#{object.exclude_end? ? object.last.to_i : object.last.to_i + 1})"
- elsif [object.first, object.last].all? { |el| NilClass === el }
- "empty"
- else
- nil
- end
- else
- object
- end
- end
-
private
HstorePair = begin
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
index 02c295983f..e09319890a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -78,6 +78,64 @@ module ActiveRecord
end
end
+ class Range < Type
+ attr_reader :subtype
+ def initialize(subtype)
+ @subtype = subtype
+ end
+
+ def extract_bounds(value)
+ from, to = value[1..-2].split(',')
+ {
+ from: (value[1] == ',' || from == '-infinity') ? infinity(:negative => true) : from,
+ to: (value[-2] == ',' || to == 'infinity') ? infinity : to,
+ exclude_start: (value[0] == '('),
+ exclude_end: (value[-1] == ')')
+ }
+ end
+
+ def infinity(options = {})
+ ::Float::INFINITY * (options[:negative] ? -1 : 1)
+ end
+
+ def infinity?(value)
+ value.respond_to?(:infinite?) && value.infinite?
+ end
+
+ def to_integer(value)
+ infinity?(value) ? value : value.to_i
+ end
+
+ def type_cast(value)
+ return if value.nil? || value == 'empty'
+ return value if value.is_a?(::Range)
+
+ extracted = extract_bounds(value)
+
+ case @subtype
+ when :date
+ from = ConnectionAdapters::Column.value_to_date(extracted[:from])
+ from -= 1.day if extracted[:exclude_start]
+ to = ConnectionAdapters::Column.value_to_date(extracted[:to])
+ when :decimal
+ from = BigDecimal.new(extracted[:from].to_s)
+ # FIXME: add exclude start for ::Range, same for timestamp ranges
+ to = BigDecimal.new(extracted[:to].to_s)
+ when :time
+ from = ConnectionAdapters::Column.string_to_time(extracted[:from])
+ to = ConnectionAdapters::Column.string_to_time(extracted[:to])
+ when :integer
+ from = to_integer(extracted[:from]) rescue value ? 1 : 0
+ from -= 1 if extracted[:exclude_start]
+ to = to_integer(extracted[:to]) rescue value ? 1 : 0
+ else
+ return value
+ end
+
+ ::Range.new(from, to, extracted[:exclude_end])
+ end
+ end
+
class Integer < Type
def type_cast(value)
return if value.nil?
@@ -168,14 +226,6 @@ module ActiveRecord
end
end
- class IntRange < Type
- def type_cast(value)
- return if value.nil?
-
- ConnectionAdapters::PostgreSQLColumn.string_to_intrange value
- end
- end
-
class TypeMap
def initialize
@mapping = {}
@@ -189,6 +239,10 @@ module ActiveRecord
@mapping[oid]
end
+ def clear
+ @mapping.clear
+ end
+
def key?(oid)
@mapping.key? oid
end
@@ -241,6 +295,13 @@ module ActiveRecord
alias_type 'int8', 'int2'
alias_type 'oid', 'int2'
+ register_type 'daterange', OID::Range.new(:date)
+ register_type 'numrange', OID::Range.new(:decimal)
+ register_type 'tsrange', OID::Range.new(:time)
+ register_type 'int4range', OID::Range.new(:integer)
+ alias_type 'tstzrange', 'tsrange'
+ alias_type 'int8range', 'int4range'
+
register_type 'numeric', OID::Decimal.new
register_type 'text', OID::Identity.new
alias_type 'varchar', 'text'
@@ -278,9 +339,6 @@ module ActiveRecord
register_type 'json', OID::Json.new
register_type 'ltree', OID::Identity.new
- register_type 'int4range', OID::IntRange.new
- alias_type 'int8range', 'int4range'
-
register_type 'cidr', OID::Cidr.new
alias_type 'inet', 'cidr'
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index c2fcef94da..47e2e3928f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -19,6 +19,12 @@ module ActiveRecord
return super unless column
case value
+ when Range
+ if /range$/ =~ column.sql_type
+ "'#{PostgreSQLColumn.range_to_string(value)}'::#{column.sql_type}"
+ else
+ super
+ end
when Array
if column.array
"'#{PostgreSQLColumn.array_to_string(value, column, self)}'"
@@ -31,11 +37,6 @@ module ActiveRecord
when 'json' then super(PostgreSQLColumn.json_to_string(value), column)
else super
end
- when Range
- case column.sql_type
- when 'int4range', 'int8range' then super(PostgreSQLColumn.intrange_to_string(value), column)
- else super
- end
when IPAddr
case column.sql_type
when 'inet', 'cidr' then super(PostgreSQLColumn.cidr_to_string(value), column)
@@ -74,6 +75,9 @@ module ActiveRecord
return super(value, column) unless column
case value
+ when Range
+ return super(value, column) unless /range$/ =~ column.sql_type
+ PostgreSQLColumn.range_to_string(value)
when NilClass
if column.array && array_member
'NULL'
@@ -94,11 +98,6 @@ module ActiveRecord
when 'json' then PostgreSQLColumn.json_to_string(value)
else super(value, column)
end
- when Range
- case column.sql_type
- when 'int4range', 'int8range' then PostgreSQLColumn.intrange_to_string(value)
- else super(value, column)
- end
when IPAddr
return super(value, column) unless ['inet','cidr'].include? column.sql_type
PostgreSQLColumn.cidr_to_string(value)
@@ -131,6 +130,10 @@ module ActiveRecord
end
end
+ def quote_table_name_for_assignment(table, attr)
+ quote_column_name(attr)
+ end
+
# Quotes column names for use in SQL queries.
def quote_column_name(name) #:nodoc:
PGconn.quote_ident(name.to_s)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 8c68576bdc..73ca2c8e61 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -417,14 +417,6 @@ module ActiveRecord
when 0..6; "timestamp(#{precision})"
else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
end
- when 'intrange'
- return 'int4range' unless limit
-
- case limit
- when 1..4; 'int4range'
- when 5..8; 'int8range'
- else raise(ActiveRecordError, "No range type has byte size #{limit}. Use a numeric with precision 0 instead.")
- end
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index b1b0467379..2af60ad0a2 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -77,6 +77,8 @@ module ActiveRecord
return default unless default
case default
+ when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
+ $1
# Numeric types
when /\A\(?(-?\d+(\.\d*)?\)?)\z/
$1
@@ -117,9 +119,6 @@ module ActiveRecord
# JSON
when /\A'(.*)'::json\z/
$1
- # int4range, int8range
- when /\A'(.*)'::int(4|8)range\z/
- $1
# Object identifier types
when /\A-?\d+\z/
$1
@@ -220,12 +219,11 @@ module ActiveRecord
# JSON type
when 'json'
:json
- # int4range, int8range types
- when 'int4range', 'int8range'
- :intrange
# Small and big integer types
when /^(?:small|big)int$/
:integer
+ when /(num|date|tstz|ts|int4|int8)range$/
+ field_type.to_sym
# Pass through all types that are not specific to PostgreSQL.
else
super
@@ -276,6 +274,30 @@ module ActiveRecord
column(args[0], 'tsvector', options)
end
+ def int4range(name, options = {})
+ column(name, 'int4range', options)
+ end
+
+ def int8range(name, options = {})
+ column(name, 'int8range', options)
+ end
+
+ def tsrange(name, options = {})
+ column(name, 'tsrange', options)
+ end
+
+ def tstzrange(name, options = {})
+ column(name, 'tstzrange', options)
+ end
+
+ def numrange(name, options = {})
+ column(name, 'numrange', options)
+ end
+
+ def daterange(name, options = {})
+ column(name, 'daterange', options)
+ end
+
def hstore(name, options = {})
column(name, 'hstore', options)
end
@@ -304,10 +326,6 @@ module ActiveRecord
column(name, 'json', options)
end
- def intrange(name, options = {})
- column(name, 'intrange', options)
- end
-
def column(name, type = nil, options = {})
super
column = self[name]
@@ -339,6 +357,12 @@ module ActiveRecord
timestamp: { name: "timestamp" },
time: { name: "time" },
date: { name: "date" },
+ daterange: { name: "daterange" },
+ numrange: { name: "numrange" },
+ tsrange: { name: "tsrange" },
+ tstzrange: { name: "tstzrange" },
+ int4range: { name: "int4range" },
+ int8range: { name: "int8range" },
binary: { name: "bytea" },
boolean: { name: "boolean" },
xml: { name: "xml" },
@@ -349,7 +373,6 @@ module ActiveRecord
macaddr: { name: "macaddr" },
uuid: { name: "uuid" },
json: { name: "json" },
- intrange: { name: "int4range" },
ltree: { name: "ltree" }
}
@@ -552,6 +575,45 @@ module ActiveRecord
true
end
+ # Returns true if pg > 9.2
+ def supports_extensions?
+ postgresql_version >= 90200
+ end
+
+ # Range datatypes weren't introduced until PostgreSQL 9.2
+ def supports_ranges?
+ postgresql_version >= 90200
+ end
+
+ def enable_extension(name)
+ exec_query("CREATE EXTENSION IF NOT EXISTS #{name}").tap {
+ reload_type_map
+ }
+ end
+
+ def disable_extension(name)
+ exec_query("DROP EXTENSION IF EXISTS #{name} CASCADE").tap {
+ reload_type_map
+ }
+ end
+
+ def extension_enabled?(name)
+ if supports_extensions?
+ res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL)",
+ 'SCHEMA'
+ res.column_types['exists'].type_cast res.rows.first.first
+ end
+ end
+
+ def extensions
+ if supports_extensions?
+ res = exec_query "SELECT extname from pg_extension", "SCHEMA"
+ res.rows.map { |r| res.column_types['extname'].type_cast r.first }
+ else
+ super
+ end
+ end
+
# Returns the configured supported identifier length supported by PostgreSQL
def table_alias_length
@table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
@@ -593,6 +655,10 @@ module ActiveRecord
@use_insert_returning
end
+ def valid_type?(type)
+ !native_database_types[type].nil?
+ end
+
protected
# Returns the version of the connected PostgreSQL server.
@@ -617,6 +683,11 @@ module ActiveRecord
private
+ def reload_type_map
+ OID::TYPE_MAP.clear
+ initialize_type_map
+ end
+
def initialize_type_map
result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
leaves, nodes = result.partition { |row| row['typelem'] == '0' }
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 11e8197293..91444950be 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -229,6 +229,10 @@ module ActiveRecord
@connection.class.quote(s)
end
+ def quote_table_name_for_assignment(table, attr)
+ quote_column_name(attr)
+ end
+
def quote_column_name(name) #:nodoc:
%Q("#{name.to_s.gsub('"', '""')}")
end
@@ -598,7 +602,6 @@ module ActiveRecord
super
end
end
-
end
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 94c6684700..63a1197a56 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -365,17 +365,18 @@ module ActiveRecord
pk = self.class.primary_key
@attributes[pk] = nil unless @attributes.key?(pk)
- @aggregation_cache = {}
- @association_cache = {}
- @attributes_cache = {}
- @previously_changed = {}
- @changed_attributes = {}
- @readonly = false
- @destroyed = false
- @marked_for_destruction = false
- @new_record = true
- @txn = nil
+ @aggregation_cache = {}
+ @association_cache = {}
+ @attributes_cache = {}
+ @previously_changed = {}
+ @changed_attributes = {}
+ @readonly = false
+ @destroyed = false
+ @marked_for_destruction = false
+ @new_record = true
+ @txn = nil
@_start_transaction_state = {}
+ @transaction = nil
end
end
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 5a4ef5991d..2958d08210 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -363,11 +363,11 @@ module ActiveRecord
#
# first:
# name: Smurf
- # *DEFAULTS
+ # <<: *DEFAULTS
#
# second:
# name: Fraggle
- # *DEFAULTS
+ # <<: *DEFAULTS
#
# Any fixture labeled "DEFAULTS" is safely ignored.
class FixtureSet
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 67339c05e5..823595a128 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -342,7 +342,7 @@ module ActiveRecord
end
def call(env)
- ActiveRecord::Base.logger.quietly do
+ ActiveRecord::Base.logger.silence do
ActiveRecord::Migration.check_pending!
end
@app.call(env)
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 3011f959a5..803cae7115 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -220,7 +220,7 @@ module ActiveRecord
save
end
end
-
+
alias update_attributes update
# Updates its receiver just like +update+ but calls <tt>save!</tt> instead
@@ -233,29 +233,29 @@ module ActiveRecord
save!
end
end
-
+
alias update_attributes! update!
- # Updates a single attribute of an object, without having to explicitly call save on that object.
- #
- # * Validation is skipped.
- # * Callbacks are skipped.
- # * updated_at/updated_on column is not updated if that column is available.
- #
- # Raises an +ActiveRecordError+ when called on new objects, or when the +name+
- # attribute is marked as readonly.
+ # Equivalent to <code>update_columns(name => value)</code>.
def update_column(name, value)
update_columns(name => value)
end
- # Updates the attributes from the passed-in hash, without having to explicitly call save on that object.
+ # Updates the attributes directly in the database issuing an UPDATE SQL
+ # statement and sets them in the receiver:
#
- # * Validation is skipped.
+ # user.update_columns(last_request_at: Time.current)
+ #
+ # This is the fastest way to update attributes because it goes straight to
+ # the database, but take into account that in consequence the regular update
+ # procedures are totally bypassed. In particular:
+ #
+ # * Validations are skipped.
# * Callbacks are skipped.
- # * updated_at/updated_on column is not updated if that column is available.
+ # * +updated_at+/+updated_on+ are not updated.
#
- # Raises an +ActiveRecordError+ when called on new objects, or when at least
- # one of the attributes is marked as readonly.
+ # This method raises an +ActiveRecord::ActiveRecordError+ when called on new
+ # objects, or when at least one of the attributes is marked as readonly.
def update_columns(attributes)
raise ActiveRecordError, "can not update on a new record object" unless persisted?
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 5ddcaee6be..f08b9c614d 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -1,4 +1,3 @@
-
module ActiveRecord
module Querying
delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, :to => :all
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 259d0ff12b..f36af7182f 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -284,8 +284,6 @@ db_namespace = namespace :db do
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
current_config = ActiveRecord::Tasks::DatabaseTasks.current_config
case current_config['adapter']
- when /mysql/, /postgresql/, /sqlite/
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(current_config, filename)
when 'oci', 'oracle'
ActiveRecord::Base.establish_connection(current_config)
File.open(filename, "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump }
@@ -296,7 +294,7 @@ db_namespace = namespace :db do
db_string = firebird_db_string(current_config)
sh "isql -a #{db_string} > #{filename}"
else
- raise "Task not supported by '#{current_config["adapter"]}'"
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(current_config, filename)
end
if ActiveRecord::Base.connection.supports_migrations?
@@ -312,8 +310,6 @@ db_namespace = namespace :db do
current_config = ActiveRecord::Tasks::DatabaseTasks.current_config
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
case current_config['adapter']
- when /mysql/, /postgresql/, /sqlite/
- ActiveRecord::Tasks::DatabaseTasks.structure_load(current_config, filename)
when 'sqlserver'
`sqlcmd -S #{current_config['host']} -d #{current_config['database']} -U #{current_config['username']} -P #{current_config['password']} -i #{filename}`
when 'oci', 'oracle'
@@ -326,7 +322,7 @@ db_namespace = namespace :db do
db_string = firebird_db_string(current_config)
sh "isql -i #{filename} #{db_string}"
else
- raise "Task not supported by '#{current_config['adapter']}'"
+ ActiveRecord::Tasks::DatabaseTasks.structure_load(current_config, filename)
end
end
@@ -384,8 +380,6 @@ db_namespace = namespace :db do
task :purge => [:environment, :load_config] do
abcs = ActiveRecord::Base.configurations
case abcs['test']['adapter']
- when /mysql/, /postgresql/, /sqlite/
- ActiveRecord::Tasks::DatabaseTasks.purge abcs['test']
when 'sqlserver'
test = abcs.deep_dup['test']
test_database = test['database']
@@ -401,7 +395,7 @@ db_namespace = namespace :db do
ActiveRecord::Base.establish_connection(:test)
ActiveRecord::Base.connection.recreate_database!
else
- raise "Task not supported by '#{abcs['test']['adapter']}'"
+ ActiveRecord::Tasks::DatabaseTasks.purge abcs['test']
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index bcfcb061f2..0995750ecd 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -1,4 +1,3 @@
-
module ActiveRecord
# = Active Record Reflection
module Reflection # :nodoc:
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 6ec5cf3e18..0053530f73 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -276,7 +276,7 @@ module ActiveRecord
stmt.table(table)
stmt.key = table[primary_key]
- if joins_values.any?
+ if with_default_scope.joins_values.any?
@klass.connection.join_to_update(stmt, arel)
else
stmt.take(arel.limit)
@@ -401,7 +401,7 @@ module ActiveRecord
stmt = Arel::DeleteManager.new(arel.engine)
stmt.from(table)
- if joins_values.any?
+ if with_default_scope.joins_values.any?
@klass.connection.join_to_delete(stmt, arel, table[primary_key])
else
stmt.wheres = arel.constraints
@@ -474,16 +474,16 @@ module ActiveRecord
# Returns sql statement for the relation.
#
- # Users.where(name: 'Oscar').to_sql
+ # User.where(name: 'Oscar').to_sql
# # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
def to_sql
@to_sql ||= klass.connection.to_sql(arel, bind_values.dup)
end
- # Returns a hash of where conditions
+ # Returns a hash of where conditions.
#
- # Users.where(name: 'Oscar').where_values_hash
- # # => {name: "oscar"}
+ # User.where(name: 'Oscar').where_values_hash
+ # # => {name: "Oscar"}
def where_values_hash
equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|
node.left.relation.name == table_name
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 3f154bd1cc..f10c290755 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -192,7 +192,8 @@ module ActiveRecord
def perform_calculation(operation, column_name, options = {})
operation = operation.to_s.downcase
- distinct = options[:distinct]
+ # If #count is used in conjuction with #uniq it is considered distinct. (eg. relation.uniq.count)
+ distinct = options[:distinct] || self.uniq_value
if operation == "count"
column_name ||= (select_for_count || :all)
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index 59226d316e..eb23e92fb8 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/hash/keys'
+require "set"
module ActiveRecord
class Relation
@@ -105,25 +106,26 @@ module ActiveRecord
end
def merged_wheres
- if values[:where]
- merged_wheres = relation.where_values + values[:where]
-
- unless relation.where_values.empty?
- # Remove equalities with duplicated left-hand. Last one wins.
- seen = {}
- merged_wheres = merged_wheres.reverse.reject { |w|
- nuke = false
- if w.respond_to?(:operator) && w.operator == :==
- nuke = seen[w.left]
- seen[w.left] = true
- end
- nuke
- }.reverse
- end
+ values[:where] ||= []
- merged_wheres
+ if values[:where].empty? || relation.where_values.empty?
+ relation.where_values + values[:where]
else
- relation.where_values
+ # Remove equalities from the existing relation with a LHS which is
+ # present in the relation being merged in.
+
+ seen = Set.new
+ values[:where].each { |w|
+ if w.respond_to?(:operator) && w.operator == :==
+ seen << w.left
+ end
+ }
+
+ relation.where_values.reject { |w|
+ w.respond_to?(:operator) &&
+ w.operator == :== &&
+ seen.include?(w.left)
+ } + values[:where]
end
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 83074e72c1..5cd015eba7 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -7,12 +7,12 @@ module ActiveRecord
table = default_table
if value.is_a?(Hash)
- table = Arel::Table.new(column, default_table.engine)
- association = klass.reflect_on_association(column.to_sym)
-
if value.empty?
- queries.concat ['1 = 2']
+ queries << '1=0'
else
+ table = Arel::Table.new(column, default_table.engine)
+ association = klass.reflect_on_association(column.to_sym)
+
value.each do |k, v|
queries.concat expand(association && association.klass, table, k, v)
end
@@ -58,7 +58,7 @@ module ActiveRecord
key
else
key = key.to_s
- key.split('.').first.to_sym if key.include?('.')
+ key.split('.').first if key.include?('.')
end
end.compact
end
@@ -66,7 +66,7 @@ module ActiveRecord
private
def self.build(attribute, value)
case value
- when Array, ActiveRecord::Associations::CollectionProxy
+ when Array
values = value.to_a.map {|x| x.is_a?(Base) ? x.id : x}
ranges, values = values.partition {|v| v.is_a?(Range)}
@@ -98,6 +98,11 @@ module ActiveRecord
when Class
# FIXME: I think we need to deprecate this behavior
attribute.eq(value.name)
+ when Integer, ActiveSupport::Duration
+ # Arel treats integers as literals, but they should be quoted when compared with strings
+ table = attribute.relation
+ column = table.engine.connection.schema_cache.columns_hash(table.name)[attribute.name.to_s]
+ attribute.eq(Arel::Nodes::SqlLiteral.new(table.engine.connection.quote(value, column)))
else
attribute.eq(value)
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 46c0d6206f..42849d6bc9 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -764,6 +764,11 @@ module ActiveRecord
[@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
when Hash
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
+
+ attributes.values.grep(ActiveRecord::Relation) do |rel|
+ self.bind_values += rel.bind_values
+ end
+
PredicateBuilder.build_from_hash(klass, attributes, table)
else
[opts]
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index 2dad1dc177..3c5b871e99 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -33,10 +33,10 @@ module ActiveRecord
# Accepts an array, hash, or string of SQL conditions and sanitizes
# them into a valid SQL fragment for a SET clause.
# { name: nil, group_id: 4 } returns "name = NULL , group_id='4'"
- def sanitize_sql_for_assignment(assignments)
+ def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
case assignments
when Array; sanitize_sql_array(assignments)
- when Hash; sanitize_sql_hash_for_assignment(assignments)
+ when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
else assignments
end
end
@@ -98,9 +98,9 @@ module ActiveRecord
# Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
# { status: nil, group_id: 1 }
# # => "status = NULL , group_id = 1"
- def sanitize_sql_hash_for_assignment(attrs)
+ def sanitize_sql_hash_for_assignment(attrs, table)
attrs.map do |attr, value|
- "#{connection.quote_column_name(attr)} = #{quote_bound_value(value)}"
+ "#{connection.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value)}"
end.join(', ')
end
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 36bde44e7c..fa9de926c5 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -24,6 +24,7 @@ module ActiveRecord
def dump(stream)
header(stream)
+ extensions(stream)
tables(stream)
trailer(stream)
stream
@@ -66,6 +67,18 @@ HEADER
stream.puts "end"
end
+ def extensions(stream)
+ return unless @connection.supports_extensions?
+ extensions = @connection.extensions
+ if extensions.any?
+ stream.puts " # These are extensions that must be enabled in order to support this database"
+ extensions.each do |extension|
+ stream.puts " enable_extension #{extension.inspect}"
+ end
+ stream.puts
+ end
+ end
+
def tables(stream)
@connection.tables.sort.each do |tbl|
next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
@@ -105,7 +118,7 @@ HEADER
# then dump all non-primary key columns
column_specs = columns.map do |column|
- raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
+ raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
next if column.name == pk
@connection.column_spec(column, @types)
end.compact
diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb
index 0c3fd1bd29..9746b1c3c2 100644
--- a/activerecord/lib/active_record/scoping.rb
+++ b/activerecord/lib/active_record/scoping.rb
@@ -1,4 +1,3 @@
-
module ActiveRecord
module Scoping
extend ActiveSupport::Concern
@@ -25,6 +24,5 @@ module ActiveRecord
send("#{att}=", value) if respond_to?("#{att}=")
end
end
-
end
end
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index 6835d0e01b..5bd481082e 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -1,4 +1,3 @@
-
module ActiveRecord
module Scoping
module Default
@@ -99,7 +98,7 @@ module ActiveRecord
)
end
- self.default_scopes = default_scopes + [scope]
+ self.default_scopes += [scope]
end
def build_default_scope # :nodoc:
@@ -140,7 +139,6 @@ module ActiveRecord
self.ignore_default_scope = false
end
end
-
end
end
end
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index cf4cf9e602..a610f479f2 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -42,21 +42,19 @@ module ActiveRecord
#
# All stored values are automatically available through accessors on the Active Record
# object, but sometimes you want to specialize this behavior. This can be done by overwriting
- # the default accessors (using the same name as the attribute) and calling
- # <tt>read_store_attribute(store_attribute_name, attr_name)</tt> and
- # <tt>write_store_attribute(store_attribute_name, attr_name, value)</tt> to actually
- # change things.
+ # the default accessors (using the same name as the attribute) and calling <tt>super</tt>
+ # to actually change things.
#
# class Song < ActiveRecord::Base
# # Uses a stored integer to hold the volume adjustment of the song
# store :settings, accessors: [:volume_adjustment]
#
# def volume_adjustment=(decibels)
- # write_store_attribute(:settings, :volume_adjustment, decibels.to_i)
+ # super(decibels.to_i)
# end
#
# def volume_adjustment
- # read_store_attribute(:settings, :volume_adjustment).to_i
+ # super.to_i
# end
# end
module Store
@@ -75,19 +73,30 @@ module ActiveRecord
def store_accessor(store_attribute, *keys)
keys = keys.flatten
- keys.each do |key|
- define_method("#{key}=") do |value|
- write_store_attribute(store_attribute, key, value)
- end
- define_method(key) do
- read_store_attribute(store_attribute, key)
+ _store_accessors_module.module_eval do
+ keys.each do |key|
+ define_method("#{key}=") do |value|
+ write_store_attribute(store_attribute, key, value)
+ end
+
+ define_method(key) do
+ read_store_attribute(store_attribute, key)
+ end
end
end
self.stored_attributes[store_attribute] ||= []
self.stored_attributes[store_attribute] |= keys
end
+
+ def _store_accessors_module
+ @_store_accessors_module ||= begin
+ mod = Module.new
+ include mod
+ mod
+ end
+ end
end
protected
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index 67c7e714e6..4fa7cf8a7d 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -1,6 +1,7 @@
module ActiveRecord
module Tasks # :nodoc:
class DatabaseAlreadyExists < StandardError; end # :nodoc:
+ class DatabaseNotSupported < StandardError; end # :nodoc:
module DatabaseTasks # :nodoc:
extend self
@@ -121,6 +122,9 @@ module ActiveRecord
def class_for_adapter(adapter)
key = @tasks.keys.detect { |pattern| adapter[pattern] }
+ unless key
+ raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
+ end
@tasks[key]
end
diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb
index c035ad43a2..e9142481a3 100644
--- a/activerecord/lib/active_record/test_case.rb
+++ b/activerecord/lib/active_record/test_case.rb
@@ -60,16 +60,17 @@ module ActiveRecord
self.clear_log
- self.ignored_sql = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
+ self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
# FIXME: this needs to be refactored so specific database can add their own
# ignored SQL, or better yet, use a different notification for the queries
# instead examining the SQL content.
oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im]
mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/]
- postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im]
+ postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i]
+ sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im]
- [oracle_ignored, mysql_ignored, postgresql_ignored].each do |db_ignored_sql|
+ [oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
ignored_sql.concat db_ignored_sql
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 4a608e4f7b..4b7a388dc7 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -286,8 +286,11 @@ module ActiveRecord
end
# Call the after_commit callbacks
+ #
+ # Ensure that it is not called if the object was never persisted (failed create),
+ # but call it after the commit of a destroyed object
def committed! #:nodoc:
- run_callbacks :commit
+ run_callbacks :commit if destroyed? || persisted?
ensure
clear_transaction_record_state
end
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 404b492288..f9149c1819 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -164,14 +164,16 @@ module ActiveRecord
class AdapterTestWithoutTransaction < ActiveRecord::TestCase
self.use_transactional_fixtures = false
+ class Klass < ActiveRecord::Base
+ end
+
def setup
- @klass = Class.new(ActiveRecord::Base)
- @klass.establish_connection 'arunit'
- @connection = @klass.connection
+ Klass.establish_connection 'arunit'
+ @connection = Klass.connection
end
def teardown
- @klass.remove_connection
+ Klass.remove_connection
end
test "transaction state is reset after a reconnect" do
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index ffd6904aec..b67d70ede7 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -1,6 +1,9 @@
require "cases/helper"
class MysqlConnectionTest < ActiveRecord::TestCase
+ class Klass < ActiveRecord::Base
+ end
+
def setup
super
@connection = ActiveRecord::Base.connection
@@ -17,9 +20,8 @@ class MysqlConnectionTest < ActiveRecord::TestCase
run_without_connection do |orig|
ar_config = ARTest.connection_config['arunit']
url = "mysql://#{ar_config["username"]}@localhost/#{ar_config["database"]}"
- klass = Class.new(ActiveRecord::Base)
- klass.establish_connection(url)
- assert_equal ar_config['database'], klass.connection.current_database
+ Klass.establish_connection(url)
+ assert_equal ar_config['database'], Klass.connection.current_database
end
end
diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
index 0eb1cc511e..0b017dc4a0 100644
--- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
@@ -16,6 +16,15 @@ module ActiveRecord
eosql
end
+ def test_valid_column
+ column = @conn.column('ex').find { |col| col.name == 'id' }
+ assert @conn.valid_type?(column.type)
+ end
+
+ def test_invalid_column
+ assert !@conn.valid_type?(:foobar)
+ end
+
def test_client_encoding
assert_equal Encoding::UTF_8, @conn.client_encoding
end
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index fa8f339f00..c03660957e 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -108,7 +108,7 @@ module ActiveRecord
@connection.verify!
new_connection_pid = @connection.query('select pg_backend_pid()')
ensure
- raw_connection_class.class_eval <<-CODE
+ raw_connection_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
alias query query_unfake
undef query_fake
CODE
diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
index 2254be8612..33c796191e 100644
--- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
@@ -3,6 +3,9 @@ require "cases/helper"
class PostgresqlArray < ActiveRecord::Base
end
+class PostgresqlRange < ActiveRecord::Base
+end
+
class PostgresqlTsvector < ActiveRecord::Base
end
@@ -43,7 +46,106 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
@connection.execute("INSERT INTO postgresql_arrays (id, commission_by_quarter, nicknames) VALUES (1, '{35000,21000,18000,17000}', '{foo,bar,baz}')")
@first_array = PostgresqlArray.find(1)
+ @connection.execute <<_SQL if @connection.supports_ranges?
+ INSERT INTO postgresql_ranges (
+ date_range,
+ num_range,
+ ts_range,
+ tstz_range,
+ int4_range,
+ int8_range
+ ) VALUES (
+ '[''2012-01-02'', ''2012-01-04'']',
+ '[0.1, 0.2]',
+ '[''2010-01-01 14:30'', ''2011-01-01 14:30'']',
+ '[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'']',
+ '[1, 10]',
+ '[10, 100]'
+ )
+_SQL
+
+ @connection.execute <<_SQL if @connection.supports_ranges?
+ INSERT INTO postgresql_ranges (
+ date_range,
+ num_range,
+ ts_range,
+ tstz_range,
+ int4_range,
+ int8_range
+ ) VALUES (
+ '(''2012-01-02'', ''2012-01-04'')',
+ '[0.1, 0.2)',
+ '[''2010-01-01 14:30'', ''2011-01-01 14:30'')',
+ '[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'')',
+ '(1, 10)',
+ '(10, 100)'
+ )
+_SQL
+
+ @connection.execute <<_SQL if @connection.supports_ranges?
+ INSERT INTO postgresql_ranges (
+ date_range,
+ num_range,
+ ts_range,
+ tstz_range,
+ int4_range,
+ int8_range
+ ) VALUES (
+ '(''2012-01-02'',]',
+ '[0.1,]',
+ '[''2010-01-01 14:30'',]',
+ '[''2010-01-01 14:30:00+05'',]',
+ '(1,]',
+ '(10,]'
+ )
+_SQL
+
+ @connection.execute <<_SQL if @connection.supports_ranges?
+ INSERT INTO postgresql_ranges (
+ date_range,
+ num_range,
+ ts_range,
+ tstz_range,
+ int4_range,
+ int8_range
+ ) VALUES (
+ '[,]',
+ '[,]',
+ '[,]',
+ '[,]',
+ '[,]',
+ '[,]'
+ )
+_SQL
+
+ @connection.execute <<_SQL if @connection.supports_ranges?
+ INSERT INTO postgresql_ranges (
+ date_range,
+ num_range,
+ ts_range,
+ tstz_range,
+ int4_range,
+ int8_range
+ ) VALUES (
+ '(''2012-01-02'', ''2012-01-02'')',
+ '(0.1, 0.1)',
+ '(''2010-01-01 14:30'', ''2010-01-01 14:30'')',
+ '(''2010-01-01 14:30:00+05'', ''2010-01-01 06:30:00-03'')',
+ '(1, 1)',
+ '(10, 10)'
+ )
+_SQL
+
+ if @connection.supports_ranges?
+ @first_range = PostgresqlRange.find(1)
+ @second_range = PostgresqlRange.find(2)
+ @third_range = PostgresqlRange.find(3)
+ @fourth_range = PostgresqlRange.find(4)
+ @empty_range = PostgresqlRange.find(5)
+ end
+
@connection.execute("INSERT INTO postgresql_tsvectors (id, text_vector) VALUES (1, ' ''text'' ''vector'' ')")
+
@first_tsvector = PostgresqlTsvector.find(1)
@connection.execute("INSERT INTO postgresql_moneys (id, wealth) VALUES (1, '567.89'::money)")
@@ -82,6 +184,16 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
assert_equal :text, @first_array.column_for_attribute(:nicknames).type
end
+ def test_data_type_of_range_types
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ assert_equal :daterange, @first_range.column_for_attribute(:date_range).type
+ assert_equal :numrange, @first_range.column_for_attribute(:num_range).type
+ assert_equal :tsrange, @first_range.column_for_attribute(:ts_range).type
+ assert_equal :tstzrange, @first_range.column_for_attribute(:tstz_range).type
+ assert_equal :int4range, @first_range.column_for_attribute(:int4_range).type
+ assert_equal :int8range, @first_range.column_for_attribute(:int8_range).type
+ end
+
def test_data_type_of_tsvector_types
assert_equal :tsvector, @first_tsvector.column_for_attribute(:text_vector).type
end
@@ -128,11 +240,201 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
assert_equal "'text' 'vector'", @first_tsvector.text_vector
end
+ def test_int4range_values
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ assert_equal 1...11, @first_range.int4_range
+ assert_equal 2...10, @second_range.int4_range
+ assert_equal 2...Float::INFINITY, @third_range.int4_range
+ assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int4_range)
+ assert_equal nil, @empty_range.int4_range
+ end
+
+ def test_int8range_values
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ assert_equal 10...101, @first_range.int8_range
+ assert_equal 11...100, @second_range.int8_range
+ assert_equal 11...Float::INFINITY, @third_range.int8_range
+ assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int8_range)
+ assert_equal nil, @empty_range.int8_range
+ end
+
+ def test_daterange_values
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ assert_equal Date.new(2012, 1, 2)...Date.new(2012, 1, 5), @first_range.date_range
+ assert_equal Date.new(2012, 1, 3)...Date.new(2012, 1, 4), @second_range.date_range
+ assert_equal Date.new(2012, 1, 3)...Float::INFINITY, @third_range.date_range
+ assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.date_range)
+ assert_equal nil, @empty_range.date_range
+ end
+
+ def test_numrange_values
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ assert_equal BigDecimal.new('0.1')..BigDecimal.new('0.2'), @first_range.num_range
+ assert_equal BigDecimal.new('0.1')...BigDecimal.new('0.2'), @second_range.num_range
+ assert_equal BigDecimal.new('0.1')...BigDecimal.new('Infinity'), @third_range.num_range
+ assert_equal BigDecimal.new('-Infinity')...BigDecimal.new('Infinity'), @fourth_range.num_range
+ assert_equal nil, @empty_range.num_range
+ end
+
+ def test_tsrange_values
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ tz = ::ActiveRecord::Base.default_timezone
+ assert_equal Time.send(tz, 2010, 1, 1, 14, 30, 0)..Time.send(tz, 2011, 1, 1, 14, 30, 0), @first_range.ts_range
+ assert_equal Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2011, 1, 1, 14, 30, 0), @second_range.ts_range
+ assert_equal Time.send(tz, 2010, 1, 1, 14, 30, 0)...Float::INFINITY, @third_range.ts_range
+ assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.ts_range)
+ assert_equal nil, @empty_range.ts_range
+ end
+
+ def test_tstzrange_values
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ assert_equal Time.parse('2010-01-01 09:30:00 UTC')..Time.parse('2011-01-01 17:30:00 UTC'), @first_range.tstz_range
+ assert_equal Time.parse('2010-01-01 09:30:00 UTC')...Time.parse('2011-01-01 17:30:00 UTC'), @second_range.tstz_range
+ assert_equal Time.parse('2010-01-01 09:30:00 UTC')...Float::INFINITY, @third_range.tstz_range
+ assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.tstz_range)
+ assert_equal nil, @empty_range.tstz_range
+ end
+
def test_money_values
assert_equal 567.89, @first_money.wealth
assert_equal(-567.89, @second_money.wealth)
end
+ def test_create_tstzrange
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ tstzrange = Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2011-02-02 14:30:00 CDT')
+ range = PostgresqlRange.new(:tstz_range => tstzrange)
+ assert range.save
+ assert range.reload
+ assert_equal range.tstz_range, tstzrange
+ assert_equal range.tstz_range, Time.parse('2010-01-01 13:30:00 UTC')...Time.parse('2011-02-02 19:30:00 UTC')
+ end
+
+ def test_update_tstzrange
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ new_tstzrange = Time.parse('2010-01-01 14:30:00 CDT')...Time.parse('2011-02-02 14:30:00 CET')
+ assert @first_range.tstz_range = new_tstzrange
+ assert @first_range.save
+ assert @first_range.reload
+ assert_equal @first_range.tstz_range, new_tstzrange
+ assert @first_range.tstz_range = Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2010-01-01 13:30:00 +0000')
+ assert @first_range.save
+ assert @first_range.reload
+ assert_equal @first_range.tstz_range, nil
+ end
+
+ def test_create_tsrange
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ tz = ::ActiveRecord::Base.default_timezone
+ tsrange = Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2011, 2, 2, 14, 30, 0)
+ range = PostgresqlRange.new(:ts_range => tsrange)
+ assert range.save
+ assert range.reload
+ assert_equal range.ts_range, tsrange
+ end
+
+ def test_update_tsrange
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ tz = ::ActiveRecord::Base.default_timezone
+ new_tsrange = Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2011, 2, 2, 14, 30, 0)
+ assert @first_range.ts_range = new_tsrange
+ assert @first_range.save
+ assert @first_range.reload
+ assert_equal @first_range.ts_range, new_tsrange
+ assert @first_range.ts_range = Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2010, 1, 1, 14, 30, 0)
+ assert @first_range.save
+ assert @first_range.reload
+ assert_equal @first_range.ts_range, nil
+ end
+
+ def test_create_numrange
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ numrange = BigDecimal.new('0.5')...BigDecimal.new('1')
+ range = PostgresqlRange.new(:num_range => numrange)
+ assert range.save
+ assert range.reload
+ assert_equal range.num_range, numrange
+ end
+
+ def test_update_numrange
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ new_numrange = BigDecimal.new('0.5')...BigDecimal.new('1')
+ assert @first_range.num_range = new_numrange
+ assert @first_range.save
+ assert @first_range.reload
+ assert_equal @first_range.num_range, new_numrange
+ assert @first_range.num_range = BigDecimal.new('0.5')...BigDecimal.new('0.5')
+ assert @first_range.save
+ assert @first_range.reload
+ assert_equal @first_range.num_range, nil
+ end
+
+ def test_create_daterange
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ daterange = Range.new(Date.new(2012, 1, 1), Date.new(2013, 1, 1), true)
+ range = PostgresqlRange.new(:date_range => daterange)
+ assert range.save
+ assert range.reload
+ assert_equal range.date_range, daterange
+ end
+
+ def test_update_daterange
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ new_daterange = Date.new(2012, 2, 3)...Date.new(2012, 2, 10)
+ assert @first_range.date_range = new_daterange
+ assert @first_range.save
+ assert @first_range.reload
+ assert_equal @first_range.date_range, new_daterange
+ assert @first_range.date_range = Date.new(2012, 2, 3)...Date.new(2012, 2, 3)
+ assert @first_range.save
+ assert @first_range.reload
+ assert_equal @first_range.date_range, nil
+ end
+
+ def test_create_int4range
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ int4range = Range.new(3, 50, true)
+ range = PostgresqlRange.new(:int4_range => int4range)
+ assert range.save
+ assert range.reload
+ assert_equal range.int4_range, int4range
+ end
+
+ def test_update_int4range
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ new_int4range = 6...10
+ assert @first_range.int4_range = new_int4range
+ assert @first_range.save
+ assert @first_range.reload
+ assert_equal @first_range.int4_range, new_int4range
+ assert @first_range.int4_range = 3...3
+ assert @first_range.save
+ assert @first_range.reload
+ assert_equal @first_range.int4_range, nil
+ end
+
+ def test_create_int8range
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ int8range = Range.new(30, 50, true)
+ range = PostgresqlRange.new(:int8_range => int8range)
+ assert range.save
+ assert range.reload
+ assert_equal range.int8_range, int8range
+ end
+
+ def test_update_int8range
+ skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
+ new_int8range = 60000...10000000
+ assert @first_range.int8_range = new_int8range
+ assert @first_range.save
+ assert @first_range.reload
+ assert_equal @first_range.int8_range, new_int8range
+ assert @first_range.int8_range = 39999...39999
+ assert @first_range.save
+ assert @first_range.reload
+ assert_equal @first_range.int8_range, nil
+ end
+
def test_update_tsvector
new_text_vector = "'new' 'text' 'vector'"
assert @first_tsvector.text_vector = new_text_vector
@@ -243,13 +545,13 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
def test_update_bit_string
new_bit_string = '11111111'
- new_bit_string_varying = 'FF'
+ new_bit_string_varying = '11111110'
assert @first_bit_string.bit_string = new_bit_string
assert @first_bit_string.bit_string_varying = new_bit_string_varying
assert @first_bit_string.save
assert @first_bit_string.reload
- assert_equal @first_bit_string.bit_string, new_bit_string
- assert_equal @first_bit_string.bit_string, @first_bit_string.bit_string_varying
+ assert_equal new_bit_string, @first_bit_string.bit_string
+ assert_equal new_bit_string_varying, @first_bit_string.bit_string_varying
end
def test_update_oid
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index 23bafde17b..6640f9b497 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -11,15 +11,23 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
def setup
@connection = ActiveRecord::Base.connection
- begin
- @connection.transaction do
- @connection.create_table('hstores') do |t|
- t.hstore 'tags', :default => ''
- end
- end
- rescue ActiveRecord::StatementInvalid
+
+ unless @connection.supports_extensions?
return skip "do not test on PG without hstore"
end
+
+ unless @connection.extension_enabled?('hstore')
+ @connection.enable_extension 'hstore'
+ @connection.commit_db_transaction
+ end
+
+ @connection.reconnect!
+
+ @connection.transaction do
+ @connection.create_table('hstores') do |t|
+ t.hstore 'tags', :default => ''
+ end
+ end
@column = Hstore.columns.find { |c| c.name == 'tags' }
end
@@ -27,6 +35,32 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
@connection.execute 'drop table if exists hstores'
end
+ def test_hstore_included_in_extensions
+ assert @connection.respond_to?(:extensions), "connection should have a list of extensions"
+ assert @connection.extensions.include?('hstore'), "extension list should include hstore"
+ end
+
+ def test_hstore_enabled
+ assert @connection.extension_enabled?('hstore')
+ end
+
+ def test_disable_hstore
+ if @connection.extension_enabled?('hstore')
+ @connection.disable_extension 'hstore'
+ assert_not @connection.extension_enabled?('hstore')
+ end
+ end
+
+ def test_enable_hstore
+ if @connection.extension_enabled?('hstore')
+ @connection.disable_extension 'hstore'
+ end
+
+ assert_not @connection.extension_enabled?('hstore')
+ @connection.enable_extension 'hstore'
+ assert @connection.extension_enabled?('hstore')
+ end
+
def test_column
assert_equal :hstore, @column.type
end
diff --git a/activerecord/test/cases/adapters/postgresql/intrange_test.rb b/activerecord/test/cases/adapters/postgresql/intrange_test.rb
deleted file mode 100644
index 5f6a64619d..0000000000
--- a/activerecord/test/cases/adapters/postgresql/intrange_test.rb
+++ /dev/null
@@ -1,106 +0,0 @@
-# encoding: utf-8
-
-require "cases/helper"
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
-
-class PostgresqlIntrangesTest < ActiveRecord::TestCase
- class IntRangeDataType < ActiveRecord::Base
- self.table_name = 'intrange_data_type'
- end
-
- def setup
- @connection = ActiveRecord::Base.connection
- begin
- @connection.transaction do
- @connection.create_table('intrange_data_type') do |t|
- t.intrange 'int_range', :default => (1..10)
- t.intrange 'long_int_range', :limit => 8, :default => (1..100)
- end
- end
- rescue ActiveRecord::StatementInvalid
- return skip "do not test on PG without ranges"
- end
- @int_range_column = IntRangeDataType.columns.find { |c| c.name == 'int_range' }
- @long_int_range_column = IntRangeDataType.columns.find { |c| c.name == 'long_int_range' }
- end
-
- def teardown
- @connection.execute 'drop table if exists intrange_data_type'
- end
-
- def test_columns
- assert_equal :intrange, @int_range_column.type
- assert_equal :intrange, @long_int_range_column.type
- end
-
- def test_type_cast_intrange
- assert @int_range_column
- assert_equal(true, @int_range_column.has_default?)
- assert_equal((1..10), @int_range_column.default)
- assert_equal("int4range", @int_range_column.sql_type)
-
- data = "[1,10)"
- hash = @int_range_column.class.string_to_intrange data
- assert_equal((1..9), hash)
- assert_equal((1..9), @int_range_column.type_cast(data))
-
- assert_equal((nil..nil), @int_range_column.type_cast("empty"))
- assert_equal((1..5), @int_range_column.type_cast('[1,5]'))
- assert_equal((2..4), @int_range_column.type_cast('(1,5)'))
- assert_equal((2..39), @int_range_column.type_cast('[2,40)'))
- assert_equal((10..20), @int_range_column.type_cast('(9,20]'))
- end
-
- def test_type_cast_long_intrange
- assert @long_int_range_column
- assert_equal(true, @long_int_range_column.has_default?)
- assert_equal((1..100), @long_int_range_column.default)
- assert_equal("int8range", @long_int_range_column.sql_type)
- end
-
- def test_rewrite
- @connection.execute "insert into intrange_data_type (int_range) VALUES ('(1, 6)')"
- x = IntRangeDataType.first
- x.int_range = (1..100)
- assert x.save!
- end
-
- def test_select
- @connection.execute "insert into intrange_data_type (int_range) VALUES ('(1, 4]')"
- x = IntRangeDataType.first
- assert_equal((2..4), x.int_range)
- end
-
- def test_empty_range
- @connection.execute %q|insert into intrange_data_type (int_range) VALUES('empty')|
- x = IntRangeDataType.first
- assert_equal((nil..nil), x.int_range)
- end
-
- def test_rewrite_to_nil
- @connection.execute %q|insert into intrange_data_type (int_range) VALUES('(1, 4]')|
- x = IntRangeDataType.first
- x.int_range = nil
- assert x.save!
- assert_equal(nil, x.int_range)
- end
-
- def test_invalid_intrange
- assert IntRangeDataType.create!(int_range: ('a'..'d'))
- x = IntRangeDataType.first
- assert_equal(nil, x.int_range)
- end
-
- def test_save_empty_range
- assert IntRangeDataType.create!(int_range: (nil..nil))
- x = IntRangeDataType.first
- assert_equal((nil..nil), x.int_range)
- end
-
- def test_save_invalid_data
- assert_raises(ActiveRecord::StatementInvalid) do
- IntRangeDataType.create!(int_range: "empty1")
- end
- end
-end
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 872204c644..d33e360bd4 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -10,6 +10,15 @@ module ActiveRecord
@connection.exec_query('create table ex(id serial primary key, number integer, data character varying(255))')
end
+ def test_valid_column
+ column = @connection.column('ex').find { |col| col.name == 'id' }
+ assert @connection.valid_type?(column.type)
+ end
+
+ def test_invalid_column
+ assert !@connection.valid_type?(:foobar)
+ end
+
def test_primary_key
assert_equal 'id', @connection.primary_key('ex')
end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index 2ba9143cd5..a7b2764fc1 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -17,7 +17,7 @@ module ActiveRecord
@conn.extend(Module.new { def logger; end })
column = Struct.new(:type, :name).new(:string, "foo")
binary = SecureRandom.hex
- expected = binary.dup.encode!('utf-8')
+ expected = binary.dup.encode!(Encoding::UTF_8)
assert_equal expected, @conn.type_cast(binary, column)
end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 003052bac4..64af49acec 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -25,6 +25,15 @@ module ActiveRecord
@conn.intercepted = true
end
+ def test_valid_column
+ column = @conn.column('items').find { |col| col.name == 'id' }
+ assert @conn.valid_type?(column.type)
+ end
+
+ def test_invalid_column
+ assert @conn.valid_type?(:foobar)
+ end
+
def teardown
@conn.intercepted = false
@conn.logged = []
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index aa5b27623e..244e0b7179 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -12,6 +12,7 @@ if ActiveRecord::Base.connection.supports_migrations?
def teardown
@connection.drop_table :fruits rescue nil
+ ActiveRecord::SchemaMigration.delete_all rescue nil
end
def test_schema_define
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index be2d30641e..3bf9125013 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1162,4 +1162,10 @@ class EagerAssociationTest < ActiveRecord::TestCase
Post.where('1 = 0').scoping { Comment.preload(:post).find(1).post }
)
end
+
+ test "preloading does not cache has many association subset when preloaded with a through association" do
+ author = Author.includes(:comments_with_order_and_conditions, :posts).first
+ assert_no_queries { assert_equal 2, author.comments_with_order_and_conditions.size }
+ assert_no_queries { assert_equal 5, author.posts.size, "should not cache a subset of the association" }
+ end
end
diff --git a/activerecord/test/cases/associations/habtm_join_table_test.rb b/activerecord/test/cases/associations/habtm_join_table_test.rb
deleted file mode 100644
index fe2b82f2c1..0000000000
--- a/activerecord/test/cases/associations/habtm_join_table_test.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'cases/helper'
-
-class MyReader < ActiveRecord::Base
- has_and_belongs_to_many :my_books
-end
-
-class MyBook < ActiveRecord::Base
- has_and_belongs_to_many :my_readers
-end
-
-class HabtmJoinTableTest < ActiveRecord::TestCase
- def setup
- ActiveRecord::Base.connection.create_table :my_books, :force => true do |t|
- t.string :name
- end
- assert ActiveRecord::Base.connection.table_exists?(:my_books)
-
- ActiveRecord::Base.connection.create_table :my_readers, :force => true do |t|
- t.string :name
- end
- assert ActiveRecord::Base.connection.table_exists?(:my_readers)
-
- ActiveRecord::Base.connection.create_table :my_books_my_readers, :force => true do |t|
- t.integer :my_book_id
- t.integer :my_reader_id
- end
- assert ActiveRecord::Base.connection.table_exists?(:my_books_my_readers)
- end
-
- def teardown
- ActiveRecord::Base.connection.drop_table :my_books
- ActiveRecord::Base.connection.drop_table :my_readers
- ActiveRecord::Base.connection.drop_table :my_books_my_readers
- 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 7e6c7d5862..fd6d531645 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -19,6 +19,7 @@ require 'models/line_item'
require 'models/car'
require 'models/bulb'
require 'models/engine'
+require 'models/categorization'
class HasManyAssociationsTestForCountWithFinderSql < ActiveRecord::TestCase
class Invoice < ActiveRecord::Base
@@ -108,7 +109,8 @@ end
class HasManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :categories, :companies, :developers, :projects,
:developers_projects, :topics, :authors, :comments,
- :people, :posts, :readers, :taggings, :cars, :essays
+ :people, :posts, :readers, :taggings, :cars, :essays,
+ :categorizations
def setup
Client.destroyed_client_ids.clear
@@ -625,6 +627,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 3, company.clients_of_firm.size
end
+ def test_collection_not_empty_after_building
+ company = companies(:first_firm)
+ assert_predicate company.contracts, :empty?
+ company.contracts.build
+ assert_not_predicate company.contracts, :empty?
+ end
+
def test_collection_size_twice_for_regressions
post = posts(:thinking)
assert_equal 0, post.readers.size
@@ -1705,4 +1714,37 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 0, post.comments.count
end
end
+
+ test "collection proxy respects default scope" do
+ author = authors(:mary)
+ assert !author.first_posts.exists?
+ end
+
+ test "association with extend option" do
+ post = posts(:welcome)
+ assert_equal "lifo", post.comments_with_extend.author
+ assert_equal "hello", post.comments_with_extend.greeting
+ end
+
+ test "association with extend option with multiple extensions" do
+ post = posts(:welcome)
+ assert_equal "lifo", post.comments_with_extend_2.author
+ assert_equal "hello", post.comments_with_extend_2.greeting
+ end
+
+ test "delete record with complex joins" do
+ david = authors(:david)
+
+ post = david.posts.first
+ post.type = 'PostWithSpecialCategorization'
+ post.save
+
+ categorization = post.categorizations.first
+ categorization.special = true
+ categorization.save
+
+ assert_not_equal [], david.posts_with_special_categorizations
+ david.posts_with_special_categorizations = []
+ assert_equal [], david.posts_with_special_categorizations
+ 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 af91fb2920..67d18f313a 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -894,4 +894,11 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ test "has many through with default scope on the target" do
+ person = people(:michael)
+ assert_equal [posts(:thinking)], person.first_posts
+
+ readers(:michael_authorless).update(first_post_id: 1)
+ assert_equal [posts(:thinking)], person.reload.first_posts
+ end
end
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 03d99d19f6..e355ed3495 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -221,6 +221,9 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_has_many_with_has_and_belongs_to_many_source_reflection_preload_via_joins
+ # preload table schemas
+ Author.joins(:post_categories).first
+
assert_includes_and_joins_equal(
Author.where('categories.id' => categories(:cooking).id),
[authors(:bob)], :post_categories
@@ -246,6 +249,9 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload_via_joins
+ # preload table schemas
+ Category.joins(:post_comments).first
+
assert_includes_and_joins_equal(
Category.where('comments.id' => comments(:more_greetings).id).order('categories.id'),
[categories(:general), categories(:technology)], :post_comments
@@ -271,6 +277,9 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload_via_joins
+ # preload table schemas
+ Author.joins(:category_post_comments).first
+
assert_includes_and_joins_equal(
Author.where('comments.id' => comments(:does_it_hurt).id).order('authors.id'),
[authors(:david), authors(:mary)], :category_post_comments
diff --git a/activerecord/test/cases/binary_test.rb b/activerecord/test/cases/binary_test.rb
index 25d2896ab0..9a486cf8b8 100644
--- a/activerecord/test/cases/binary_test.rb
+++ b/activerecord/test/cases/binary_test.rb
@@ -23,7 +23,7 @@ unless current_adapter?(:SybaseAdapter, :DB2Adapter, :FirebirdAdapter)
# Mysql adapter doesn't properly encode things, so we have to do it
if current_adapter?(:MysqlAdapter)
- name.force_encoding('UTF-8')
+ name.force_encoding(Encoding::UTF_8)
end
assert_equal 'いただきます!', name
end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index b7622705bf..be49e948fc 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -341,6 +341,10 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 5, Account.count(:firm_id)
end
+ def test_count_with_uniq
+ assert_equal 4, Account.select(:credit_limit).uniq.count
+ end
+
def test_count_with_column_and_options_parameter
assert_equal 2, Account.where("credit_limit = 50 AND firm_id IS NOT NULL").count(:firm_id)
end
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 0718d0886f..23e64bee7e 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -327,6 +327,17 @@ module ActiveRecord
def test_pool_sets_connection_visitor
assert @pool.connection.visitor.is_a?(Arel::Visitors::ToSql)
end
+
+ # make sure exceptions are thrown when establish_connection
+ # is called with a anonymous class
+ def test_anonymous_class_exception
+ anonymous = Class.new(ActiveRecord::Base)
+ handler = ActiveRecord::Base.connection_handler
+
+ assert_raises(RuntimeError) {
+ handler.establish_connection anonymous, nil
+ }
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
index 52de0efe7f..f0a2cdca1a 100644
--- a/activerecord/test/cases/connection_specification/resolver_test.rb
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -8,40 +8,66 @@ module ActiveRecord
Resolver.new(spec, {}).spec.config
end
+ def test_url_invalid_adapter
+ assert_raises(LoadError) do
+ resolve 'ridiculous://foo?encoding=utf8'
+ end
+ end
+
+ # The abstract adapter is used simply to bypass the bit of code that
+ # checks that the adapter file can be required in.
+
def test_url_host_no_db
- skip "only if mysql is available" unless current_adapter?(:MysqlAdapter, :Mysql2Adapter)
- spec = resolve 'mysql://foo?encoding=utf8'
+ spec = resolve 'abstract://foo?encoding=utf8'
assert_equal({
- :adapter => "mysql",
+ :adapter => "abstract",
:host => "foo",
:encoding => "utf8" }, spec)
end
def test_url_host_db
- skip "only if mysql is available" unless current_adapter?(:MysqlAdapter, :Mysql2Adapter)
- spec = resolve 'mysql://foo/bar?encoding=utf8'
+ spec = resolve 'abstract://foo/bar?encoding=utf8'
assert_equal({
- :adapter => "mysql",
+ :adapter => "abstract",
:database => "bar",
:host => "foo",
:encoding => "utf8" }, spec)
end
def test_url_port
- skip "only if mysql is available" unless current_adapter?(:MysqlAdapter, :Mysql2Adapter)
- spec = resolve 'mysql://foo:123?encoding=utf8'
+ spec = resolve 'abstract://foo:123?encoding=utf8'
assert_equal({
- :adapter => "mysql",
+ :adapter => "abstract",
:port => 123,
:host => "foo",
:encoding => "utf8" }, spec)
end
+ def test_url_query_numeric
+ spec = resolve 'abstract://foo:123?encoding=utf8&int=500&float=10.9'
+ assert_equal({
+ :adapter => "abstract",
+ :port => 123,
+ :int => 500,
+ :float => 10.9,
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+
+ def test_url_query_boolean
+ spec = resolve 'abstract://foo:123?true=true&false=false'
+ assert_equal({
+ :adapter => "abstract",
+ :port => 123,
+ :true => true,
+ :false => false,
+ :host => "foo" }, spec)
+ end
+
def test_encoded_password
- skip "only if mysql is available" unless current_adapter?(:MysqlAdapter, :Mysql2Adapter)
password = 'am@z1ng_p@ssw0rd#!'
encoded_password = URI.encode_www_form_component(password)
- spec = resolve "mysql://foo:#{encoded_password}@localhost/bar"
+ spec = resolve "abstract://foo:#{encoded_password}@localhost/bar"
assert_equal password, spec[:password]
end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index b9961a4420..c7d2ba6073 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -79,6 +79,8 @@ class DirtyTest < ActiveRecord::TestCase
assert pirate.created_on_changed?
assert_kind_of ActiveSupport::TimeWithZone, pirate.created_on_was
assert_equal old_created_on, pirate.created_on_was
+ pirate.created_on = old_created_on
+ assert !pirate.created_on_changed?
end
end
@@ -551,18 +553,17 @@ class DirtyTest < ActiveRecord::TestCase
end
end
- def test_setting_time_attributes_with_time_zone_field_to_same_time_should_not_be_marked_as_a_change
+ def test_datetime_attribute_can_be_updated_with_fractional_seconds
in_time_zone 'Paris' do
target = Class.new(ActiveRecord::Base)
- target.table_name = 'pirates'
+ target.table_name = 'topics'
- created_on = Time.now
+ written_on = Time.utc(2012, 12, 1, 12, 0, 0).in_time_zone('Paris')
- pirate = target.create(:created_on => created_on)
- pirate.reload # Here mysql truncate the usec value to 0
+ topic = target.create(:written_on => written_on)
+ topic.written_on += 0.3
- pirate.created_on = created_on
- assert !pirate.created_on_changed?
+ assert topic.written_on_changed?, 'Fractional second update not detected'
end
end
diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb
index 4e2adff344..eca500f7e4 100644
--- a/activerecord/test/cases/dup_test.rb
+++ b/activerecord/test/cases/dup_test.rb
@@ -108,18 +108,20 @@ module ActiveRecord
end
def test_dup_validity_is_independent
- Topic.validates_presence_of :title
- topic = Topic.new("title" => "Litterature")
- topic.valid?
-
- duped = topic.dup
- duped.title = nil
- assert duped.invalid?
-
- topic.title = nil
- duped.title = 'Mathematics'
- assert topic.invalid?
- assert duped.valid?
+ repair_validations(Topic) do
+ Topic.validates_presence_of :title
+ topic = Topic.new("title" => "Litterature")
+ topic.valid?
+
+ duped = topic.dup
+ duped.title = nil
+ assert duped.invalid?
+
+ topic.title = nil
+ duped.title = 'Mathematics'
+ assert topic.invalid?
+ assert duped.valid?
+ end
end
end
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 5ffb32e809..7dbb6616f8 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -15,6 +15,8 @@ require 'support/connection'
# TODO: Move all these random hacks into the ARTest namespace and into the support/ dir
+Thread.abort_on_exception = true
+
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index 5ac4a16f33..cad759bba9 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -35,7 +35,7 @@ module ActiveRecord
t.column :foo, :string
end
- assert_equal %w(foo id), connection.columns(:testings).map(&:name).sort
+ assert_equal %w(id foo), connection.columns(:testings).map(&:name)
end
def test_create_table_with_not_null_column
@@ -119,7 +119,7 @@ module ActiveRecord
t.column :foo, :string
end
- assert_equal %w(foo testing_id), connection.columns(:testings).map(&:name).sort
+ assert_equal %w(testing_id foo), connection.columns(:testings).map(&:name)
end
def test_create_table_with_primary_key_prefix_as_table_name
@@ -129,7 +129,7 @@ module ActiveRecord
t.column :foo, :string
end
- assert_equal %w(foo testingid), connection.columns(:testings).map(&:name).sort
+ assert_equal %w(testingid foo), connection.columns(:testings).map(&:name)
end
def test_create_table_raises_when_redefining_primary_key_column
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 187c6e8447..fa8dec0e15 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -30,9 +30,13 @@ class MigrationTest < ActiveRecord::TestCase
Reminder.reset_column_information
ActiveRecord::Migration.verbose = true
ActiveRecord::Migration.message_count = 0
+ ActiveRecord::Base.connection.schema_cache.clear!
end
def teardown
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
+
ActiveRecord::Base.connection.initialize_schema_migrations_table
ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
@@ -44,6 +48,7 @@ class MigrationTest < ActiveRecord::TestCase
%w(reminders people_reminders prefix_reminders_suffix).each do |table|
Reminder.connection.drop_table(table) rescue nil
end
+ Reminder.reset_table_name
Reminder.reset_column_information
%w(last_name key bio age height wealth birthday favorite_day
@@ -257,9 +262,6 @@ class MigrationTest < ActiveRecord::TestCase
ActiveRecord::Base.table_name_suffix = ""
Reminder.reset_table_name
assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
- ensure
- ActiveRecord::Base.table_name_prefix = ""
- ActiveRecord::Base.table_name_suffix = ""
end
def test_proper_table_name
@@ -286,9 +288,6 @@ class MigrationTest < ActiveRecord::TestCase
Reminder.reset_table_name
assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
- ActiveRecord::Base.table_name_prefix = ""
- ActiveRecord::Base.table_name_suffix = ""
- Reminder.reset_table_name
end
def test_rename_table_with_prefix_and_suffix
@@ -307,8 +306,6 @@ class MigrationTest < ActiveRecord::TestCase
assert_equal "hello world", Thing.first.content
ensure
- ActiveRecord::Base.table_name_prefix = ''
- ActiveRecord::Base.table_name_suffix = ''
Thing.reset_table_name
Thing.reset_sequence_name
end
@@ -326,9 +323,6 @@ class MigrationTest < ActiveRecord::TestCase
WeNeedReminders.down
assert_raise(ActiveRecord::StatementInvalid) { Reminder.first }
ensure
- ActiveRecord::Base.table_name_prefix = ''
- ActiveRecord::Base.table_name_suffix = ''
- Reminder.reset_table_name
Reminder.reset_sequence_name
end
@@ -437,6 +431,8 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
def setup
@connection = Person.connection
@connection.create_table(:delete_me, :force => true) {|t| }
+ Person.reset_column_information
+ Person.reset_sequence_name
end
def teardown
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index b936cca875..08dbf19e7b 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -399,6 +399,12 @@ class PersistencesTest < ActiveRecord::TestCase
assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_attribute(:color, 'black') }
end
+ def test_string_ids
+ mv = Minivan.where(:minivan_id => 1234).first_or_initialize
+ assert mv.new_record?
+ assert_equal '1234', mv.minivan_id
+ end
+
def test_update_attribute_with_one_updated
t = Topic.first
t.update_attribute(:title, 'super_title')
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index 3dd11ae89d..0ad05223d4 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -122,35 +122,35 @@ module ActiveRecord
def test_quote_float
float = 1.2
assert_equal float.to_s, @quoter.quote(float, nil)
- assert_equal float.to_s, @quoter.quote(float, Object.new)
+ assert_equal float.to_s, @quoter.quote(float, FakeColumn.new(:float))
end
def test_quote_fixnum
fixnum = 1
assert_equal fixnum.to_s, @quoter.quote(fixnum, nil)
- assert_equal fixnum.to_s, @quoter.quote(fixnum, Object.new)
+ assert_equal fixnum.to_s, @quoter.quote(fixnum, FakeColumn.new(:integer))
end
def test_quote_bignum
bignum = 1 << 100
assert_equal bignum.to_s, @quoter.quote(bignum, nil)
- assert_equal bignum.to_s, @quoter.quote(bignum, Object.new)
+ assert_equal bignum.to_s, @quoter.quote(bignum, FakeColumn.new(:integer))
end
def test_quote_bigdecimal
bigdec = BigDecimal.new((1 << 100).to_s)
assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, nil)
- assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, Object.new)
+ assert_equal bigdec.to_s('F'), @quoter.quote(bigdec, FakeColumn.new(:decimal))
end
def test_dates_and_times
@quoter.extend(Module.new { def quoted_date(value) 'lol' end })
assert_equal "'lol'", @quoter.quote(Date.today, nil)
- assert_equal "'lol'", @quoter.quote(Date.today, Object.new)
+ assert_equal "'lol'", @quoter.quote(Date.today, FakeColumn.new(:date))
assert_equal "'lol'", @quoter.quote(Time.now, nil)
- assert_equal "'lol'", @quoter.quote(Time.now, Object.new)
+ assert_equal "'lol'", @quoter.quote(Time.now, FakeColumn.new(:time))
assert_equal "'lol'", @quoter.quote(DateTime.now, nil)
- assert_equal "'lol'", @quoter.quote(DateTime.now, Object.new)
+ assert_equal "'lol'", @quoter.quote(DateTime.now, FakeColumn.new(:datetime))
end
def test_crazy_object
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index f69a248491..53cdf89b1f 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -8,7 +8,20 @@ require 'models/edge'
module ActiveRecord
class WhereTest < ActiveRecord::TestCase
- fixtures :posts, :edges
+ fixtures :posts, :edges, :authors
+
+ def test_where_copies_bind_params
+ author = authors(:david)
+ posts = author.posts.where('posts.id != 1')
+ joined = Post.where(id: posts)
+
+ assert_operator joined.length, :>, 0
+
+ joined.each { |post|
+ assert_equal author, post.author
+ assert_not_equal 1, post.id
+ }
+ end
def test_belongs_to_shallow_where
author = Author.new
@@ -95,5 +108,30 @@ module ActiveRecord
assert_equal 4, Edge.where(blank).order("sink_id").to_a.size
end
end
+
+ def test_where_with_integer_for_string_column
+ count = Post.where(:title => 0).count
+ assert_equal 0, count
+ end
+
+ def test_where_with_float_for_string_column
+ count = Post.where(:title => 0.0).count
+ assert_equal 0, count
+ end
+
+ def test_where_with_boolean_for_string_column
+ count = Post.where(:title => false).count
+ assert_equal 0, count
+ end
+
+ def test_where_with_decimal_for_string_column
+ count = Post.where(:title => BigDecimal.new(0)).count
+ assert_equal 0, count
+ end
+
+ def test_where_with_duration_for_string_column
+ count = Post.where(:title => 0.seconds).count
+ assert_equal 0, count
+ end
end
end
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index 78fb91d321..8e6c38706f 100644
--- a/activerecord/test/cases/relation_scoping_test.rb
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -161,6 +161,28 @@ class RelationScopingTest < ActiveRecord::TestCase
assert !Developer.all.where_values.include?("name = 'Jamis'")
end
+
+ def test_default_scope_filters_on_joins
+ assert_equal 1, DeveloperFilteredOnJoins.all.count
+ assert_equal DeveloperFilteredOnJoins.all.first, developers(:david).becomes(DeveloperFilteredOnJoins)
+ end
+
+ def test_update_all_default_scope_filters_on_joins
+ DeveloperFilteredOnJoins.update_all(:salary => 65000)
+ assert_equal 65000, Developer.find(developers(:david).id).salary
+
+ # has not changed jamis
+ assert_not_equal 65000, Developer.find(developers(:jamis).id).salary
+ end
+
+ def test_delete_all_default_scope_filters_on_joins
+ assert_not_equal [], DeveloperFilteredOnJoins.all
+
+ DeveloperFilteredOnJoins.delete_all()
+
+ assert_equal [], DeveloperFilteredOnJoins.all
+ assert_not_equal [], Developer.all
+ end
end
class NestedRelationScopingTest < ActiveRecord::TestCase
@@ -369,19 +391,19 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_default_scope_with_inheritance
wheres = InheritedPoorDeveloperCalledJamis.all.where_values_hash
assert_equal "Jamis", wheres[:name]
- assert_equal 50000, wheres[:salary]
+ assert_equal Arel.sql("50000"), wheres[:salary]
end
def test_default_scope_with_module_includes
wheres = ModuleIncludedPoorDeveloperCalledJamis.all.where_values_hash
assert_equal "Jamis", wheres[:name]
- assert_equal 50000, wheres[:salary]
+ assert_equal Arel.sql("50000"), wheres[:salary]
end
def test_default_scope_with_multiple_calls
wheres = MultiplePoorDeveloperCalledJamis.all.where_values_hash
assert_equal "Jamis", wheres[:name]
- assert_equal 50000, wheres[:salary]
+ assert_equal Arel.sql("50000"), wheres[:salary]
end
def test_scope_overwrites_default
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 3a499a2025..379c0c0758 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -404,6 +404,13 @@ class RelationTest < ActiveRecord::TestCase
end
end
+ def test_preload_applies_to_all_chained_preloaded_scopes
+ assert_queries(3) do
+ post = Post.with_comments.with_tags.first
+ assert post
+ end
+ end
+
def test_find_with_included_associations
assert_queries(2) do
posts = Post.includes(:comments).order('posts.id')
@@ -1481,4 +1488,17 @@ class RelationTest < ActiveRecord::TestCase
Array.send(:remove_method, :__omg__)
end
end
+
+ test "merge collapses wheres from the LHS only" do
+ left = Post.where(title: "omg").where(comments_count: 1)
+ right = Post.where(title: "wtf").where(title: "bbq")
+
+ expected = [left.where_values[1]] + right.where_values
+ merged = left.merge(right)
+
+ assert_equal expected, merged.where_values
+ assert !merged.to_sql.include?("omg")
+ assert merged.to_sql.include?("wtf")
+ assert merged.to_sql.include?("bbq")
+ end
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index cae12e0e3a..bfecc0d1e9 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -1,6 +1,5 @@
require "cases/helper"
-
class SchemaDumperTest < ActiveRecord::TestCase
def setup
super
@@ -231,6 +230,21 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
if current_adapter?(:PostgreSQLAdapter)
+ def test_schema_dump_includes_extensions
+ connection = ActiveRecord::Base.connection
+ skip unless connection.supports_extensions?
+
+ connection.stubs(:extensions).returns(['hstore'])
+ output = standard_dump
+ assert_match "# These are extensions that must be enabled", output
+ assert_match %r{enable_extension "hstore"}, output
+
+ connection.stubs(:extensions).returns([])
+ output = standard_dump
+ assert_no_match "# These are extensions that must be enabled", output
+ assert_no_match %r{enable_extension}, output
+ end
+
def test_schema_dump_includes_xml_shorthand_definition
output = standard_dump
if %r{create_table "postgresql_xml_data_type"} =~ output
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index 43bf285ba9..3e32d866ee 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -35,6 +35,12 @@ class StoreTest < ActiveRecord::TestCase
assert_equal '(123) 456-7890', @john.phone_number
end
+ test "overriding a read accessor using super" do
+ @john.settings[:color] = nil
+
+ assert_equal 'red', @john.color
+ end
+
test "updating the store will mark it as changed" do
@john.color = 'red'
assert @john.settings_changed?
@@ -66,6 +72,12 @@ class StoreTest < ActiveRecord::TestCase
assert_equal '1234567890', @john.settings[:phone_number]
end
+ test "overriding a write accessor using super" do
+ @john.color = 'yellow'
+
+ assert_equal 'blue', @john.color
+ end
+
test "preserve store attributes data in HashWithIndifferentAccess format without any conversion" do
@john.json_data = ActiveSupport::HashWithIndifferentAccess.new(:height => 'tall', 'weight' => 'heavy')
@john.height = 'low'
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index 659d5eae72..3bfbc92afd 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -31,6 +31,12 @@ module ActiveRecord
ActiveRecord::Tasks::DatabaseTasks.register_task(/foo/, klazz)
ActiveRecord::Tasks::DatabaseTasks.structure_dump({'adapter' => :foo}, "awesome-file.sql")
end
+
+ def test_unregistered_task
+ assert_raise(ActiveRecord::Tasks::DatabaseNotSupported) do
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump({'adapter' => :bar}, "awesome-file.sql")
+ end
+ end
end
class DatabaseTasksCreateTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index 869892e33f..fd5651b4e0 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -5,9 +5,29 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false
fixtures :topics
+ class ReplyWithCallbacks < ActiveRecord::Base
+ self.table_name = :topics
+
+ belongs_to :topic, foreign_key: "parent_id"
+
+ validates_presence_of :content
+
+ after_commit :do_after_commit, on: :create
+
+ def history
+ @history ||= []
+ end
+
+ def do_after_commit
+ history << :commit_on_create
+ end
+ end
+
class TopicWithCallbacks < ActiveRecord::Base
self.table_name = :topics
+ has_many :replies, class_name: "ReplyWithCallbacks", foreign_key: "parent_id"
+
after_commit{|record| record.send(:do_after_commit, nil)}
after_commit(:on => :create){|record| record.send(:do_after_commit, :create)}
after_commit(:on => :update){|record| record.send(:do_after_commit, :update)}
@@ -93,6 +113,13 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
assert_equal [:commit_on_create], @new_record.history
end
+ def test_only_call_after_commit_on_create_after_transaction_commits_for_new_record_if_create_succeeds_creating_through_association
+ topic = TopicWithCallbacks.create!(:title => "New topic", :written_on => Date.today)
+ reply = topic.replies.create
+
+ assert_equal [], reply.history
+ end
+
def test_call_after_rollback_after_transaction_rollsback
@first.after_commit_block{|r| r.history << :after_commit}
@first.after_rollback_block{|r| r.history << :after_rollback}
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index bcbc48b38a..546737b398 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -451,6 +451,34 @@ class TransactionTest < ActiveRecord::TestCase
end
end
+ def test_transactions_state_from_rollback
+ connection = Topic.connection
+ transaction = ActiveRecord::ConnectionAdapters::ClosedTransaction.new(connection).begin
+
+ assert transaction.open?
+ assert !transaction.state.rolledback?
+ assert !transaction.state.committed?
+
+ transaction.perform_rollback
+
+ assert transaction.state.rolledback?
+ assert !transaction.state.committed?
+ end
+
+ def test_transactions_state_from_commit
+ connection = Topic.connection
+ transaction = ActiveRecord::ConnectionAdapters::ClosedTransaction.new(connection).begin
+
+ assert transaction.open?
+ assert !transaction.state.rolledback?
+ assert !transaction.state.committed?
+
+ transaction.perform_commit
+
+ assert !transaction.state.rolledback?
+ assert transaction.state.committed?
+ end
+
private
%w(validation save destroy).each do |filter|
diff --git a/activerecord/test/fixtures/readers.yml b/activerecord/test/fixtures/readers.yml
index 8a6076655b..14b883f041 100644
--- a/activerecord/test/fixtures/readers.yml
+++ b/activerecord/test/fixtures/readers.yml
@@ -2,8 +2,10 @@ michael_welcome:
id: 1
post_id: 1
person_id: 1
+ first_post_id: 2
michael_authorless:
id: 2
post_id: 3
- person_id: 1 \ No newline at end of file
+ person_id: 1
+ first_post_id: 3
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index 467f3ccd39..4c3b71e8f9 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -1,10 +1,24 @@
class Admin::User < ActiveRecord::Base
+ class Coder
+ def initialize(default = {})
+ @default = default
+ end
+
+ def dump(o)
+ ActiveSupport::JSON.encode(o || @default)
+ end
+
+ def load(s)
+ s.present? ? ActiveSupport::JSON.decode(s) : @default.clone
+ end
+ end
+
belongs_to :account
store :settings, :accessors => [ :color, :homepage ]
store_accessor :settings, :favorite_food
store :preferences, :accessors => [ :remember_login ]
- store :json_data, :accessors => [ :height, :weight ], :coder => JSON
- store :json_data_empty, :accessors => [ :is_a_good_guy ], :coder => JSON
+ store :json_data, :accessors => [ :height, :weight ], :coder => Coder.new
+ store :json_data_empty, :accessors => [ :is_a_good_guy ], :coder => Coder.new
def phone_number
read_store_attribute(:settings, :phone_number).gsub(/(\d{3})(\d{3})(\d{4})/,'(\1) \2-\3')
@@ -13,4 +27,13 @@ class Admin::User < ActiveRecord::Base
def phone_number=(value)
write_store_attribute(:settings, :phone_number, value && value.gsub(/[^\d]/,''))
end
+
+ def color
+ super || 'red'
+ end
+
+ def color=(value)
+ value = 'blue' unless %w(black red green blue).include?(value)
+ super
+ end
end
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 6935cfb0ea..8423411474 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -9,17 +9,8 @@ class Author < ActiveRecord::Base
has_many :posts_with_categories, -> { includes(:categories) }, :class_name => "Post"
has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order("posts.id") }, :class_name => "Post"
has_many :posts_containing_the_letter_a, :class_name => "Post"
- has_many :posts_with_extension, :class_name => "Post" do #, :extend => ProxyTestExtension
- def testing_proxy_owner
- proxy_owner
- end
- def testing_proxy_reflection
- proxy_reflection
- end
- def testing_proxy_target
- proxy_target
- end
- end
+ has_many :posts_with_special_categorizations, :class_name => 'PostWithSpecialCategorization'
+ has_many :posts_with_extension, :class_name => "Post"
has_one :post_about_thinking, -> { where("posts.title like '%thinking%'") }, :class_name => 'Post'
has_one :post_about_thinking_with_last_comment, -> { where("posts.title like '%thinking%'").includes(:last_comment) }, :class_name => 'Post'
has_many :comments, :through => :posts
diff --git a/activerecord/test/models/category.rb b/activerecord/test/models/category.rb
index f8c8ebb70c..7da39a8e33 100644
--- a/activerecord/test/models/category.rb
+++ b/activerecord/test/models/category.rb
@@ -31,9 +31,4 @@ class Category < ActiveRecord::Base
end
class SpecialCategory < Category
-
- def self.what_are_you
- 'a special category...'
- end
-
end
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
index 4b2015fe01..ede5fbd0c6 100644
--- a/activerecord/test/models/comment.rb
+++ b/activerecord/test/models/comment.rb
@@ -29,16 +29,10 @@ class Comment < ActiveRecord::Base
end
class SpecialComment < Comment
- def self.what_are_you
- 'a special comment...'
- end
end
class SubSpecialComment < SpecialComment
end
class VerySpecialComment < Comment
- def self.what_are_you
- 'a very special comment...'
- end
end
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 683cb54a10..81bc87bd42 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -101,6 +101,15 @@ class DeveloperWithIncludes < ActiveRecord::Base
default_scope { includes(:audit_logs) }
end
+class DeveloperFilteredOnJoins < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, -> { order('projects.id') }, :foreign_key => 'developer_id', :join_table => 'developers_projects'
+
+ def self.default_scope
+ joins(:projects).where(:projects => { :name => 'Active Controller' })
+ end
+end
+
class DeveloperOrderedBySalary < ActiveRecord::Base
self.table_name = 'developers'
default_scope { order('salary DESC') }
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index c602ca5eac..fa717ef8d6 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -15,6 +15,7 @@ class Person < ActiveRecord::Base
has_many :fixed_bad_references, -> { where :favourite => true }, :class_name => 'BadReference'
has_one :favourite_reference, -> { where 'favourite=?', true }, :class_name => 'Reference'
has_many :posts_with_comments_sorted_by_comment_id, -> { includes(:comments).order('comments.id') }, :through => :readers, :source => :post
+ has_many :first_posts, -> { where(id: [1, 2]) }, through: :readers
has_many :jobs, :through => :references
has_many :jobs_with_dependent_destroy, :source => :job, :through => :references, :dependent => :destroy
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index c995f59a15..93a7a2073c 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -5,16 +5,18 @@ class Post < ActiveRecord::Base
end
end
+ module NamedExtension2
+ def greeting
+ "hello"
+ end
+ end
+
scope :containing_the_letter_a, -> { where("body LIKE '%a%'") }
scope :ranked_by_comments, -> { order("comments_count DESC") }
scope :limit_by, lambda {|l| limit(l) }
- belongs_to :author do
- def greeting
- "hello"
- end
- end
+ belongs_to :author
belongs_to :author_with_posts, -> { includes(:posts) }, :class_name => "Author", :foreign_key => :author_id
belongs_to :author_with_address, -> { includes(:author_address) }, :class_name => "Author", :foreign_key => :author_id
@@ -29,6 +31,9 @@ class Post < ActiveRecord::Base
scope :with_very_special_comments, -> { joins(:comments).where(:comments => {:type => 'VerySpecialComment'}) }
scope :with_post, ->(post_id) { joins(:comments).where(:comments => { :post_id => post_id }) }
+ scope :with_comments, -> { preload(:comments) }
+ scope :with_tags, -> { preload(:taggings) }
+
has_many :comments do
def find_most_recent
order("id DESC").first
@@ -43,6 +48,14 @@ class Post < ActiveRecord::Base
end
end
+ has_many :comments_with_extend, extend: NamedExtension, class_name: "Comment", foreign_key: "post_id" do
+ def greeting
+ "hello"
+ end
+ end
+
+ has_many :comments_with_extend_2, extend: [NamedExtension, NamedExtension2], class_name: "Comment", foreign_key: "post_id"
+
has_many :author_favorites, :through => :author
has_many :author_categorizations, :through => :author, :source => :categorizations
has_many :author_addresses, :through => :author
@@ -151,18 +164,6 @@ class SubStiPost < StiPost
self.table_name = Post.table_name
end
-ActiveSupport::Deprecation.silence do
- class DeprecatedPostWithComment < ActiveRecord::Base
- self.table_name = 'posts'
- default_scope where("posts.comments_count > 0").order("posts.comments_count ASC")
- end
-end
-
-class PostForAuthor < ActiveRecord::Base
- self.table_name = 'posts'
- cattr_accessor :selected_author
-end
-
class FirstPost < ActiveRecord::Base
self.table_name = 'posts'
default_scope { where(:id => 1) }
@@ -177,6 +178,11 @@ class PostWithDefaultInclude < ActiveRecord::Base
has_many :comments, :foreign_key => :post_id
end
+class PostWithSpecialCategorization < Post
+ has_many :categorizations, :foreign_key => :post_id
+ default_scope { where(:type => 'PostWithSpecialCategorization').joins(:categorizations).where(:categorizations => { :special => true }) }
+end
+
class PostWithDefaultScope < ActiveRecord::Base
self.table_name = 'posts'
default_scope { order(:title) }
diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb
index af3ec4be83..90273adafc 100644
--- a/activerecord/test/models/project.rb
+++ b/activerecord/test/models/project.rb
@@ -40,7 +40,4 @@ class Project < ActiveRecord::Base
end
class SpecialProject < Project
- def hello_world
- "hello there!"
- end
end
diff --git a/activerecord/test/models/reader.rb b/activerecord/test/models/reader.rb
index f8fb9c573e..3a6b7fad34 100644
--- a/activerecord/test/models/reader.rb
+++ b/activerecord/test/models/reader.rb
@@ -2,6 +2,7 @@ class Reader < ActiveRecord::Base
belongs_to :post
belongs_to :person, :inverse_of => :readers
belongs_to :single_person, :class_name => 'Person', :foreign_key => :person_id, :inverse_of => :reader
+ belongs_to :first_post, -> { where(id: [2, 3]) }
end
class SecureReader < ActiveRecord::Base
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index f7f4cebc5a..17035bf338 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -108,6 +108,7 @@ class ImportantTopic < Topic
end
class BlankTopic < Topic
+ # declared here to make sure that dynamic finder with a bang can find a model that responds to `blank?`
def blank?
true
end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index ae13f2cd8a..83b50030bd 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -1,7 +1,7 @@
ActiveRecord::Schema.define do
- %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_bit_strings postgresql_uuids postgresql_ltrees
- postgresql_oids postgresql_xml_data_type defaults geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent postgresql_json_data_type postgresql_intrange_data_type).each do |table_name|
+ %w(postgresql_ranges postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_bit_strings postgresql_uuids postgresql_ltrees
+ postgresql_oids postgresql_xml_data_type defaults geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent postgresql_json_data_type).each do |table_name|
execute "DROP TABLE IF EXISTS #{quote_table_name table_name}"
end
@@ -73,6 +73,18 @@ _SQL
);
_SQL
+ execute <<_SQL if supports_ranges?
+ CREATE TABLE postgresql_ranges (
+ id SERIAL PRIMARY KEY,
+ date_range daterange,
+ num_range numrange,
+ ts_range tsrange,
+ tstz_range tstzrange,
+ int4_range int4range,
+ int8_range int8range
+ );
+_SQL
+
execute <<_SQL
CREATE TABLE postgresql_tsvectors (
id SERIAL PRIMARY KEY,
@@ -106,16 +118,6 @@ _SQL
);
_SQL
end
-
- if 't' == select_value("select 'int4range'=ANY(select typname from pg_type)")
- execute <<_SQL
- CREATE TABLE postgresql_intrange_data_type (
- id SERIAL PRIMARY KEY,
- int_range int4range,
- int_long_range int8range
- );
-_SQL
- end
execute <<_SQL
CREATE TABLE postgresql_moneys (
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 46219c53db..d789b6cb7a 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -540,6 +540,8 @@ ActiveRecord::Schema.define do
create_table :price_estimates, :force => true do |t|
t.string :estimate_of_type
t.integer :estimate_of_id
+ t.string :thing_type
+ t.integer :thing_id
t.integer :price
end
@@ -567,6 +569,7 @@ ActiveRecord::Schema.define do
t.integer :post_id, :null => false
t.integer :person_id, :null => false
t.boolean :skimmer, :default => false
+ t.integer :first_post_id
end
create_table :references, :force => true do |t|
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 2c0d1de70f..5f7559b5a6 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,24 @@
## Rails 4.0.0 (unreleased) ##
+* ActiveSupport::Gzip.compress allows two optional arguments for compression
+ level and strategy.
+
+ *Beyond*
+
+* Modify `TimeWithZone#as_json` to include 3 decimal places of sub-second accuracy
+ by default, which is optional as per the ISO8601 spec, but extremely useful. Also
+ the default behaviour of Date#toJSON() in recent versions of Chrome, Safari and
+ Firefox.
+
+ *James Harton*
+
+* Improve `String#squish` to handle Unicode whitespace. *Antoine Lyset*
+
+* Standardise on `to_time` returning an instance of `Time` in the local system timezone
+ across `String`, `Time`, `Date`, `DateTime` and `ActiveSupport::TimeWithZone`.
+
+ *Andrew White*
+
* Extract `ActiveSupport::Testing::Performance` into https://github.com/rails/rails-perftest
You can add the gem to your Gemfile to keep using performance tests.
@@ -7,9 +26,8 @@
*Yves Senn*
-
-* Hash.from_xml raises when it encounters type="symbol" or type="yaml".
- Use Hash.from_trusted_xml to parse this XML.
+* `Hash.from_xml` raises when it encounters `type="symbol"` or `type="yaml"`.
+ Use `Hash.from_trusted_xml` to parse this XML.
CVE-2013-0156
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index 5a5548d567..edbe697962 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -275,34 +275,14 @@ module ActiveSupport
if block_given?
options = merged_options(options)
key = namespaced_key(name, options)
- unless options[:force]
- entry = instrument(:read, name, options) do |payload|
- payload[:super_operation] = :fetch if payload
- read_entry(key, options)
- end
- end
- if entry && entry.expired?
- race_ttl = options[:race_condition_ttl].to_i
- if race_ttl && (Time.now - entry.expires_at <= race_ttl)
- # When an entry has :race_condition_ttl defined, put the stale entry back into the cache
- # for a brief period while the entry is begin recalculated.
- entry.expires_at = Time.now + race_ttl
- write_entry(key, entry, :expires_in => race_ttl * 2)
- else
- delete_entry(key, options)
- end
- entry = nil
- end
+
+ cached_entry = find_cached_entry(key, name, options) unless options[:force]
+ entry = handle_expired_entry(cached_entry, key, options)
if entry
- instrument(:fetch_hit, name, options) { |payload| }
- entry.value
+ get_entry_value(entry, name, options)
else
- result = instrument(:generate, name, options) do |payload|
- yield(name)
- end
- write(name, result, options)
- result
+ save_block_result_to_cache(name, options) { |_name| yield _name }
end
else
read(name, options)
@@ -531,6 +511,42 @@ module ActiveSupport
return unless logger && logger.debug? && !silence?
logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
end
+
+ def find_cached_entry(key, name, options)
+ instrument(:read, name, options) do |payload|
+ payload[:super_operation] = :fetch if payload
+ read_entry(key, options)
+ end
+ end
+
+ def handle_expired_entry(entry, key, options)
+ if entry && entry.expired?
+ race_ttl = options[:race_condition_ttl].to_i
+ if race_ttl && (Time.now - entry.expires_at <= race_ttl)
+ # When an entry has :race_condition_ttl defined, put the stale entry back into the cache
+ # for a brief period while the entry is begin recalculated.
+ entry.expires_at = Time.now + race_ttl
+ write_entry(key, entry, :expires_in => race_ttl * 2)
+ else
+ delete_entry(key, options)
+ end
+ entry = nil
+ end
+ entry
+ end
+
+ def get_entry_value(entry, name, options)
+ instrument(:fetch_hit, name, options) { |payload| }
+ entry.value
+ end
+
+ def save_block_result_to_cache(name, options)
+ result = instrument(:generate, name, options) do |payload|
+ yield(name)
+ end
+ write(name, result, options)
+ result
+ end
end
# This class is used to represent cache entries. Cache entries have a value and an optional
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 712db2c75a..512296554f 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -7,6 +7,7 @@ end
require 'digest/md5'
require 'active_support/core_ext/marshal'
+require 'active_support/core_ext/array/extract_options'
module ActiveSupport
module Cache
@@ -158,7 +159,7 @@ module ActiveSupport
# characters properly.
def escape_key(key)
key = key.to_s.dup
- key = key.force_encoding("BINARY")
+ key = key.force_encoding(Encoding::ASCII_8BIT)
key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
key
diff --git a/activesupport/lib/active_support/core_ext/date_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
index 4e4852a5e6..1d3682eaf2 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -43,7 +43,7 @@ class DateTime
# Returns a new DateTime where one or more of the elements have been changed
# according to the +options+ parameter. The time options (<tt>:hour</tt>,
- # <tt>:minute</tt>, <tt>:sec</tt>) reset cascadingly, so if only the hour is
+ # <tt>:min</tt>, <tt>:sec</tt>) reset cascadingly, so if only the hour is
# passed, then minute and sec is set to 0. If the hour and minute is passed,
# then sec is set to 0. The +options+ parameter takes a hash with any of these
# keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>,
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index 1079ddde98..534bbe3c42 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -1,35 +1,43 @@
class Object
- # Invokes the public method identified by the symbol +method+, passing it any arguments
- # and/or the block specified, just like the regular Ruby <tt>Object#public_send</tt> does.
+ # Invokes the public method whose name goes as first argument just like
+ # +public_send+ does, except that if the receiver does not respond to it the
+ # call returns +nil+ rather than raising an exception.
#
- # *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
- # and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
+ # This method is defined to be able to write
#
- # This is also true if the receiving object does not implemented the tried method. It will
- # return +nil+ in that case as well.
- #
- # If try is called without a method to call, it will yield any given block with the object.
+ # @person.try(:name)
#
- # Please also note that +try+ is defined on +Object+, therefore it won't work with
- # subclasses of +BasicObject+. For example, using try with +SimpleDelegator+ will
- # delegate +try+ to target instead of calling it on delegator itself.
+ # instead of
#
- # Without +try+
- # @person && @person.name
- # or
# @person ? @person.name : nil
#
- # With +try+
- # @person.try(:name)
+ # +try+ returns +nil+ when called on +nil+ regardless of whether it responds
+ # to the method:
+ #
+ # nil.try(:to_i) # => nil, rather than 0
+ #
+ # Arguments and blocks are forwarded to the method if invoked:
+ #
+ # @posts.try(:each_slice, 2) do |a, b|
+ # ...
+ # end
+ #
+ # The number of arguments in the signature must match. If the object responds
+ # to the method the call is attempted and +ArgumentError+ is still raised
+ # otherwise.
#
- # +try+ also accepts arguments and/or a block, for the method it is trying
- # Person.try(:find, 1)
- # @people.try(:collect) {|p| p.name}
+ # If +try+ is called without arguments it yields the receiver to a given
+ # block unless it is +nil+:
#
- # Without a method argument try will yield to the block unless the receiver is nil.
- # @person.try { |p| "#{p.first_name} #{p.last_name}" }
+ # @person.try do |p|
+ # ...
+ # end
#
- # +try+ behaves like +Object#public_send+, unless called on +NilClass+.
+ # Please also note that +try+ is defined on +Object+, therefore it won't work
+ # with instances of classes that do not have +Object+ among their ancestors,
+ # like direct subclasses of +BasicObject+. For example, using +try+ with
+ # +SimpleDelegator+ will delegate +try+ to the target instead of calling it on
+ # delegator itself.
def try(*a, &b)
if a.empty? && block_given?
yield self
diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index c795df124b..428fa1f826 100644
--- a/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -3,26 +3,36 @@ require 'active_support/core_ext/time/calculations'
class String
# Converts a string to a Time value.
- # The +form+ can be either :utc or :local (default :utc).
+ # The +form+ can be either :utc or :local (default :local).
#
- # The time is parsed using Date._parse method.
- # If +form+ is :local, then time is formatted using Time.zone
+ # The time is parsed using Time.parse method.
+ # If +form+ is :local, then the time is in the system timezone.
+ # If the date part is missing then the current date is used and if
+ # the time part is missing then it is assumed to be 00:00:00.
#
- # "3-2-2012".to_time # => 2012-02-03 00:00:00 UTC
- # "12:20".to_time # => ArgumentError: invalid date
- # "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 UTC
- # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 UTC
- # "2012-12-13T06:12".to_time(:local) # => 2012-12-13 06:12:00 +0100
- def to_time(form = :utc)
- unless blank?
- date_values = ::Date._parse(self, false).
- values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset).
- map! { |arg| arg || 0 }
- date_values[6] *= 1000000
- offset = date_values.pop
+ # "13-12-2012".to_time # => 2012-12-13 00:00:00 +0100
+ # "06:12".to_time # => 2012-12-13 06:12:00 +0100
+ # "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100
+ # "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100
+ # "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 05:12:00 UTC
+ def to_time(form = :local)
+ parts = Date._parse(self, false)
+ return if parts.empty?
- ::Time.send(form, *date_values) - offset
- end
+ now = Time.now
+ offset = parts[:offset]
+ utc_offset = form == :utc ? 0 : now.utc_offset
+ adjustment = offset ? offset - utc_offset : 0
+
+ Time.send(
+ form,
+ parts.fetch(:year, now.year),
+ parts.fetch(:mon, now.month),
+ parts.fetch(:mday, now.day),
+ parts.fetch(:hour, 0),
+ parts.fetch(:min, 0),
+ parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0)
+ ) - adjustment
end
# Converts a string to a Date value.
@@ -42,13 +52,6 @@ class String
# "2012-12-13 12:50".to_datetime #=> Thu, 13 Dec 2012 12:50:00 +0000
# "12/13/2012".to_datetime #=> ArgumentError: invalid date
def to_datetime
- unless blank?
- date_values = ::Date._parse(self, false).
- values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :sec_fraction).
- map! { |arg| arg || 0 }
- date_values[5] += date_values.pop
-
- ::DateTime.civil(*date_values)
- end
+ ::DateTime.parse(self, false) unless blank?
end
end
diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb
index e05447439a..a1b3f79748 100644
--- a/activesupport/lib/active_support/core_ext/string/filters.rb
+++ b/activesupport/lib/active_support/core_ext/string/filters.rb
@@ -3,6 +3,8 @@ class String
# the string, and then changing remaining consecutive whitespace
# groups into one space each.
#
+ # Note that it handles both ASCII and Unicode whitespace like mongolian vowel separator (U+180E).
+ #
# %{ Multi-line
# string }.squish # => "Multi-line string"
# " foo bar \n \t boo".squish # => "foo bar boo"
@@ -12,8 +14,9 @@ class String
# Performs a destructive squish. See String#squish.
def squish!
- strip!
- gsub!(/\s+/, ' ')
+ gsub!(/\A[[:space:]]+/, '')
+ gsub!(/[[:space:]]+\z/, '')
+ gsub!(/[[:space:]]+/, ' ')
self
end
diff --git a/activesupport/lib/active_support/gzip.rb b/activesupport/lib/active_support/gzip.rb
index 6ef33ab683..b837c879bb 100644
--- a/activesupport/lib/active_support/gzip.rb
+++ b/activesupport/lib/active_support/gzip.rb
@@ -25,9 +25,9 @@ module ActiveSupport
end
# Compresses a string using gzip.
- def self.compress(source)
+ def self.compress(source, level=Zlib::DEFAULT_COMPRESSION, strategy=Zlib::DEFAULT_STRATEGY)
output = Stream.new
- gz = Zlib::GzipWriter.new(output)
+ gz = Zlib::GzipWriter.new(output, level, strategy)
gz.write(source)
gz.close
output.string
diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb
index 188653bd9b..22521a8e93 100644
--- a/activesupport/lib/active_support/i18n.rb
+++ b/activesupport/lib/active_support/i18n.rb
@@ -1,4 +1,7 @@
begin
+ require 'active_support/core_ext/hash/deep_merge'
+ require 'active_support/core_ext/hash/except'
+ require 'active_support/core_ext/hash/slice'
require 'i18n'
require 'active_support/lazy_load_hooks'
rescue LoadError => e
diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb
index 9cf4b2b2ba..c96debb93f 100644
--- a/activesupport/lib/active_support/inflector/inflections.rb
+++ b/activesupport/lib/active_support/inflector/inflections.rb
@@ -128,17 +128,29 @@ module ActiveSupport
def irregular(singular, plural)
@uncountables.delete(singular)
@uncountables.delete(plural)
- if singular[0,1].upcase == plural[0,1].upcase
- plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
- plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
- singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
+
+ s0 = singular[0]
+ srest = singular[1..-1]
+
+ p0 = plural[0]
+ prest = plural[1..-1]
+
+ if s0.upcase == p0.upcase
+ plural(/(#{s0})#{srest}$/i, '\1' + prest)
+ plural(/(#{p0})#{prest}$/i, '\1' + prest)
+
+ singular(/(#{s0})#{srest}$/i, '\1' + srest)
+ singular(/(#{p0})#{prest}$/i, '\1' + srest)
else
- plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
- plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
- plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
- plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
- singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
- singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
+ plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest)
+ plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest)
+ plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest)
+ plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest)
+
+ singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest)
+ singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest)
+ singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest)
+ singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest)
end
end
diff --git a/activesupport/lib/active_support/locale/en.yml b/activesupport/lib/active_support/locale/en.yml
index f4900dc935..a4563ace8f 100644
--- a/activesupport/lib/active_support/locale/en.yml
+++ b/activesupport/lib/active_support/locale/en.yml
@@ -16,9 +16,9 @@ en:
abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
# Used in date_select and datetime_select.
order:
- - :year
- - :month
- - :day
+ - year
+ - month
+ - day
time:
formats:
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index b7dc0689b0..ce40a7d689 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -1,5 +1,6 @@
require 'openssl'
require 'base64'
+require 'active_support/core_ext/array/extract_options'
module ActiveSupport
# MessageEncryptor is a simple way to encrypt values which get stored
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index f49ca47f14..cbc1608349 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -11,7 +11,7 @@ module ActiveSupport
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
# The Unicode version that is supported by the implementation
- UNICODE_VERSION = '6.1.0'
+ UNICODE_VERSION = '6.2.0'
# The default normalization used for operations that require
# normalization. It can be set to any of the normalizations
diff --git a/activesupport/lib/active_support/proxy_object.rb b/activesupport/lib/active_support/proxy_object.rb
index a2bdf1d790..20a0fd8e62 100644
--- a/activesupport/lib/active_support/proxy_object.rb
+++ b/activesupport/lib/active_support/proxy_object.rb
@@ -5,7 +5,7 @@ module ActiveSupport
undef_method :==
undef_method :equal?
- # Let ActiveSupport::BasicObject at least raise exceptions.
+ # Let ActiveSupport::ProxyObject at least raise exceptions.
def raise(*args)
::Object.send(:raise, *args)
end
diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb
index 72ac597d99..133aa6a054 100644
--- a/activesupport/lib/active_support/railtie.rb
+++ b/activesupport/lib/active_support/railtie.rb
@@ -13,6 +13,20 @@ module ActiveSupport
end
end
+ # Sets the default value for Time.zone
+ # If assigned value cannot be matched to a TimeZone, an exception will be raised.
+ initializer "active_support.initialize_time_zone" do |app|
+ require 'active_support/core_ext/time/zones'
+ zone_default = Time.find_zone!(app.config.time_zone)
+
+ unless zone_default
+ raise 'Value assigned to config.time_zone not recognized. ' \
+ 'Run "rake -D time" for a list of tasks for finding appropriate time zone names.'
+ end
+
+ Time.zone_default = zone_default
+ end
+
# Sets the default week start
# If assigned value is not a valid day symbol (e.g. :sunday, :monday, ...), an exception will be raised.
initializer "active_support.initialize_beginning_of_week" do |app|
@@ -28,21 +42,5 @@ module ActiveSupport
ActiveSupport.send(k, v) if ActiveSupport.respond_to? k
end
end
-
- # Sets the default value for Time.zone after initialization since the default configuration
- # lives in application initializers.
- # If assigned value cannot be matched to a TimeZone, an exception will be raised.
- config.after_initialize do |app|
- require 'active_support/core_ext/time/zones'
- zone_default = Time.find_zone!(app.config.time_zone)
-
- unless zone_default
- raise 'Value assigned to config.time_zone not recognized. ' \
- 'Run "rake -D time" for a list of tasks for finding appropriate time zone names.'
- end
-
- Time.zone_default = zone_default
- end
-
end
end
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index aa87598926..dca91e8b75 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -1,4 +1,9 @@
require 'rbconfig'
+begin
+ require 'minitest/parallel_each'
+rescue LoadError
+end
+
module ActiveSupport
module Testing
class RemoteError < StandardError
@@ -43,32 +48,36 @@ module ActiveSupport
module Isolation
require 'thread'
- class ParallelEach
- include Enumerable
+ # Recent versions of MiniTest (such as the one shipped with Ruby 2.0) already define
+ # a ParallelEach class.
+ unless defined? ParallelEach
+ class ParallelEach
+ include Enumerable
- # default to 2 cores
- CORES = (ENV['TEST_CORES'] || 2).to_i
+ # default to 2 cores
+ CORES = (ENV['TEST_CORES'] || 2).to_i
- def initialize list
- @list = list
- @queue = SizedQueue.new CORES
- end
+ def initialize list
+ @list = list
+ @queue = SizedQueue.new CORES
+ end
- def grep pattern
- self.class.new super
- end
+ def grep pattern
+ self.class.new super
+ end
- def each
- threads = CORES.times.map {
- Thread.new {
- while job = @queue.pop
- yield job
- end
+ def each
+ threads = CORES.times.map {
+ Thread.new {
+ while job = @queue.pop
+ yield job
+ end
+ }
}
- }
- @list.each { |i| @queue << i }
- CORES.times { @queue << nil }
- threads.each(&:join)
+ @list.each { |i| @queue << i }
+ CORES.times { @queue << nil }
+ threads.each(&:join)
+ end
end
end
@@ -84,10 +93,14 @@ module ActiveSupport
!ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
end
+ @@class_setup_mutex = Mutex.new
+
def _run_class_setup # class setup method should only happen in parent
- unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST']
- self.class.setup if self.class.respond_to?(:setup)
- @@ran_class_setup = true
+ @@class_setup_mutex.synchronize do
+ unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST']
+ self.class.setup if self.class.respond_to?(:setup)
+ @@ran_class_setup = true
+ end
end
end
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index d3741845d2..0e6d12a186 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -154,7 +154,7 @@ module ActiveSupport
# # => "2005/02/01 15:15:10 +0000"
def as_json(options = nil)
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
- xmlschema
+ xmlschema(3)
else
%(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
end
@@ -317,9 +317,9 @@ module ActiveSupport
end
alias_method :tv_sec, :to_i
- # A TimeWithZone acts like a Time, so just return +self+.
+ # Return an instance of Time in the system timezone.
def to_time
- utc
+ utc.to_time
end
def to_datetime
diff --git a/activesupport/lib/active_support/values/unicode_tables.dat b/activesupport/lib/active_support/values/unicode_tables.dat
index df17a8cccf..2571faa019 100644
--- a/activesupport/lib/active_support/values/unicode_tables.dat
+++ b/activesupport/lib/active_support/values/unicode_tables.dat
Binary files differ
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index 90e50f235b..dd17cb64f4 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -21,5 +21,7 @@ require 'empty_bool'
ENV['NO_RELOAD'] = '1'
require 'active_support'
+Thread.abort_on_exception = true
+
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index ec47d0632c..f3fa96ec6f 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -33,8 +33,12 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
end
def test_to_time
- assert_equal Time.local(2005, 2, 21), Date.new(2005, 2, 21).to_time
- assert_equal Time.local(2039, 2, 21), Date.new(2039, 2, 21).to_time
+ with_env_tz 'US/Eastern' do
+ assert_equal Time, Date.new(2005, 2, 21).to_time.class
+ assert_equal Time.local(2005, 2, 21), Date.new(2005, 2, 21).to_time
+ assert_equal Time.local(2005, 2, 21).utc_offset, Date.new(2005, 2, 21).to_time.utc_offset
+ end
+
silence_warnings do
0.upto(138) do |year|
[:utc, :local].each do |format|
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 54bbdbb18f..24e62cc2b9 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -41,11 +41,14 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_to_time
- assert_equal Time.utc(2005, 2, 21, 10, 11, 12), DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time
- assert_equal Time.utc(2039, 2, 21, 10, 11, 12), DateTime.new(2039, 2, 21, 10, 11, 12, 0).to_time
- # DateTimes with offsets other than 0 are returned unaltered
- assert_equal DateTime.new(2005, 2, 21, 10, 11, 12, Rational(-5, 24)), DateTime.new(2005, 2, 21, 10, 11, 12, Rational(-5, 24)).to_time
- # Fractional seconds are preserved
+ with_env_tz 'US/Eastern' do
+ assert_equal Time, DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time.class
+ assert_equal Time.local(2005, 2, 21, 5, 11, 12), DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time
+ assert_equal Time.local(2005, 2, 21, 5, 11, 12).utc_offset, DateTime.new(2005, 2, 21, 10, 11, 12, 0).to_time.utc_offset
+ end
+ end
+
+ def test_to_time_preserves_fractional_seconds
assert_equal Time.utc(2005, 2, 21, 10, 11, 12, 256), DateTime.new(2005, 2, 21, 10, 11, 12 + Rational(256, 1000000), 0).to_time
end
@@ -242,6 +245,10 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
end
end
+ def test_acts_like_date
+ assert DateTime.new.acts_like_date?
+ end
+
def test_acts_like_time
assert DateTime.new.acts_like_time?
end
diff --git a/activesupport/test/core_ext/kernel_test.rb b/activesupport/test/core_ext/kernel_test.rb
index 1583c1fa32..b8951de402 100644
--- a/activesupport/test/core_ext/kernel_test.rb
+++ b/activesupport/test/core_ext/kernel_test.rb
@@ -38,6 +38,18 @@ class KernelTest < ActiveSupport::TestCase
# Skip if we can't STDERR.tell
end
+ def test_quietly
+ old_stdout_position, old_stderr_position = STDOUT.tell, STDERR.tell
+ quietly do
+ puts 'see me, feel me'
+ STDERR.puts 'touch me, heal me'
+ end
+ assert_equal old_stdout_position, STDOUT.tell
+ assert_equal old_stderr_position, STDERR.tell
+ rescue Errno::ESPIPE
+ # Skip if we can't STDERR.tell
+ end
+
def test_silence_stderr_with_return_value
assert_equal 1, silence_stderr { 1 }
end
diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb
index ec7dd6d4fb..8d748791e3 100644
--- a/activesupport/test/core_ext/object_and_class_ext_test.rb
+++ b/activesupport/test/core_ext/object_and_class_ext_test.rb
@@ -120,6 +120,10 @@ class ObjectTryTest < ActiveSupport::TestCase
assert_raise(NoMethodError) { @string.try!(method, 'llo', 'y') }
end
+ def test_try_only_block_bang
+ assert_equal @string.reverse, @string.try! { |s| s.reverse }
+ end
+
def test_valid_method
assert_equal 5, @string.try(:size)
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index e0ddeab548..bff155f045 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -87,6 +87,12 @@ class StringInflectionsTest < ActiveSupport::TestCase
assert_equal('capital', 'Capital'.camelize(:lower))
end
+ def test_dasherize
+ UnderscoresToDashes.each do |underscored, dasherized|
+ assert_equal(dasherized, underscored.dasherize)
+ end
+ end
+
def test_underscore
CamelToUnderscore.each do |camel, underscore|
assert_equal(underscore, camel.underscore)
@@ -223,10 +229,11 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
def test_string_squish
- original = %{ A string with tabs(\t\t), newlines(\n\n), and
- many spaces( ). }
+ original = %{\u180E\u180E A string surrounded by unicode mongolian vowel separators,
+ with tabs(\t\t), newlines(\n\n), unicode nextlines(\u0085\u0085) and many spaces( ). \u180E\u180E}
- expected = "A string with tabs( ), newlines( ), and many spaces( )."
+ expected = "A string surrounded by unicode mongolian vowel separators, " +
+ "with tabs( ), newlines( ), unicode nextlines( ) and many spaces( )."
# Make sure squish returns what we expect:
assert_equal original.squish, expected
@@ -263,8 +270,8 @@ class StringInflectionsTest < ActiveSupport::TestCase
end
def test_truncate_multibyte
- assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'),
- "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8').truncate(10)
+ assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding(Encoding::UTF_8),
+ "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding(Encoding::UTF_8).truncate(10)
end
def test_truncate_should_not_be_html_safe
@@ -286,14 +293,37 @@ end
class StringConversionsTest < ActiveSupport::TestCase
def test_string_to_time
- assert_equal Time.utc(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time
- assert_equal Time.local(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time(:local)
- assert_equal Time.utc(2005, 2, 27, 23, 50, 19, 275038), "2005-02-27T23:50:19.275038".to_time
- assert_equal Time.local(2005, 2, 27, 23, 50, 19, 275038), "2005-02-27T23:50:19.275038".to_time(:local)
- assert_equal DateTime.civil(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time
- assert_equal Time.local(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time(:local)
- assert_equal Time.utc(2011, 2, 27, 23, 50), "2011-02-27 22:50 -0100".to_time
- assert_nil "".to_time
+ with_env_tz "US/Eastern" do
+ assert_equal Time.utc(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time(:utc)
+ assert_equal Time.local(2005, 2, 27, 23, 50), "2005-02-27 23:50".to_time
+ assert_equal Time.utc(2005, 2, 27, 23, 50, 19, 275038), "2005-02-27T23:50:19.275038".to_time(:utc)
+ assert_equal Time.local(2005, 2, 27, 23, 50, 19, 275038), "2005-02-27T23:50:19.275038".to_time
+ assert_equal Time.utc(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time(:utc)
+ assert_equal Time.local(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time
+ assert_equal Time.local(2011, 2, 27, 18, 50), "2011-02-27 22:50 -0100".to_time
+ assert_equal Time.utc(2011, 2, 27, 23, 50), "2011-02-27 22:50 -0100".to_time(:utc)
+ assert_equal Time.local(2005, 2, 27, 23, 50), "2005-02-27 23:50 -0500".to_time
+ assert_nil "".to_time
+ end
+ end
+
+ def test_string_to_time_utc_offset
+ with_env_tz "US/Eastern" do
+ assert_equal 0, "2005-02-27 23:50".to_time(:utc).utc_offset
+ assert_equal(-18000, "2005-02-27 23:50".to_time.utc_offset)
+ assert_equal 0, "2005-02-27 22:50 -0100".to_time(:utc).utc_offset
+ assert_equal(-18000, "2005-02-27 22:50 -0100".to_time.utc_offset)
+ end
+ end
+
+ def test_partial_string_to_time
+ with_env_tz "US/Eastern" do
+ now = Time.now
+ assert_equal Time.local(now.year, now.month, now.day, 23, 50), "23:50".to_time
+ assert_equal Time.utc(now.year, now.month, now.day, 23, 50), "23:50".to_time(:utc)
+ assert_equal Time.local(now.year, now.month, now.day, 18, 50), "22:50 -0100".to_time
+ assert_equal Time.utc(now.year, now.month, now.day, 23, 50), "22:50 -0100".to_time(:utc)
+ end
end
def test_string_to_datetime
@@ -304,11 +334,25 @@ class StringConversionsTest < ActiveSupport::TestCase
assert_nil "".to_datetime
end
+ def test_partial_string_to_datetime
+ now = DateTime.now
+ assert_equal DateTime.civil(now.year, now.month, now.day, 23, 50), "23:50".to_datetime
+ assert_equal DateTime.civil(now.year, now.month, now.day, 23, 50, 0, "-04:00"), "23:50 -0400".to_datetime
+ end
+
def test_string_to_date
assert_equal Date.new(2005, 2, 27), "2005-02-27".to_date
assert_nil "".to_date
assert_equal Date.new(Date.today.year, 2, 3), "Feb 3rd".to_date
end
+
+ protected
+ def with_env_tz(new_tz = 'US/Eastern')
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+ yield
+ ensure
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
+ end
end
class StringBehaviourTest < ActiveSupport::TestCase
@@ -318,22 +362,24 @@ class StringBehaviourTest < ActiveSupport::TestCase
end
class CoreExtStringMultibyteTest < ActiveSupport::TestCase
- UNICODE_STRING = 'こにちわ'
- ASCII_STRING = 'ohayo'
- BYTE_STRING = "\270\236\010\210\245"
+ UTF8_STRING = 'こにちわ'
+ ASCII_STRING = 'ohayo'.encode('US-ASCII')
+ EUC_JP_STRING = 'さよなら'.encode('EUC-JP')
+ INVALID_UTF8_STRING = "\270\236\010\210\245"
def test_core_ext_adds_mb_chars
- assert_respond_to UNICODE_STRING, :mb_chars
+ assert_respond_to UTF8_STRING, :mb_chars
end
def test_string_should_recognize_utf8_strings
- assert UNICODE_STRING.is_utf8?
+ assert UTF8_STRING.is_utf8?
assert ASCII_STRING.is_utf8?
- assert !BYTE_STRING.is_utf8?
+ assert !EUC_JP_STRING.is_utf8?
+ assert !INVALID_UTF8_STRING.is_utf8?
end
def test_mb_chars_returns_instance_of_proxy_class
- assert_kind_of ActiveSupport::Multibyte.proxy_class, UNICODE_STRING.mb_chars
+ assert_kind_of ActiveSupport::Multibyte.proxy_class, UTF8_STRING.mb_chars
end
end
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index a2fefee3b8..43c92003dc 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -526,7 +526,11 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_to_time
- assert_equal Time.local(2005, 2, 21, 17, 44, 30), Time.local(2005, 2, 21, 17, 44, 30).to_time
+ with_env_tz 'US/Eastern' do
+ assert_equal Time, Time.local(2005, 2, 21, 17, 44, 30).to_time.class
+ assert_equal Time.local(2005, 2, 21, 17, 44, 30), Time.local(2005, 2, 21, 17, 44, 30).to_time
+ assert_equal Time.local(2005, 2, 21, 17, 44, 30).utc_offset, Time.local(2005, 2, 21, 17, 44, 30).to_time.utc_offset
+ end
end
# NOTE: this test seems to fail (changeset 1958) only on certain platforms,
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index 6c773770f0..c2b3676aac 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -75,7 +75,7 @@ class TimeWithZoneTest < ActiveSupport::TestCase
def test_to_json_with_use_standard_json_time_format_config_set_to_true
old, ActiveSupport.use_standard_json_time_format = ActiveSupport.use_standard_json_time_format, true
- assert_equal "\"1999-12-31T19:00:00-05:00\"", ActiveSupport::JSON.encode(@twz)
+ assert_equal "\"1999-12-31T19:00:00.000-05:00\"", ActiveSupport::JSON.encode(@twz)
ensure
ActiveSupport.use_standard_json_time_format = old
end
@@ -327,7 +327,11 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_to_time
- assert_equal @twz, @twz.to_time
+ with_env_tz 'US/Eastern' do
+ assert_equal Time, @twz.to_time.class
+ assert_equal Time.local(1999, 12, 31, 19), @twz.to_time
+ assert_equal Time.local(1999, 12, 31, 19).utc_offset, @twz.to_time.utc_offset
+ end
end
def test_to_date
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index c1a468ec86..9616e42f44 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -75,6 +75,11 @@ class DeprecationTest < ActiveSupport::TestCase
end
end
+ def test_deprecate_object
+ deprecated_object = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(Object.new, ':bomb:')
+ assert_deprecated(/:bomb:/) { deprecated_object.to_s }
+ end
+
def test_nil_behavior_is_ignored
ActiveSupport::Deprecation.behavior = nil
assert_deprecated(/foo=nil/) { @dtc.partially }
@@ -139,6 +144,7 @@ class DeprecationTest < ActiveSupport::TestCase
def test_deprecated_constant_proxy
assert_not_deprecated { Deprecatee::B::C }
assert_deprecated('Deprecatee::A') { assert_equal Deprecatee::B::C, Deprecatee::A }
+ assert_not_deprecated { assert_equal Deprecatee::B::C.class, Deprecatee::A.class }
end
def test_assert_deprecation_without_match
diff --git a/activesupport/test/gzip_test.rb b/activesupport/test/gzip_test.rb
index 75a0505899..0e3cf3b429 100644
--- a/activesupport/test/gzip_test.rb
+++ b/activesupport/test/gzip_test.rb
@@ -4,6 +4,12 @@ require 'active_support/core_ext/object/blank'
class GzipTest < ActiveSupport::TestCase
def test_compress_should_decompress_to_the_same_value
assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World"))
+ assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", Zlib::NO_COMPRESSION))
+ assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", Zlib::BEST_SPEED))
+ assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", Zlib::BEST_COMPRESSION))
+ assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", nil, Zlib::FILTERED))
+ assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", nil, Zlib::HUFFMAN_ONLY))
+ assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World", nil, nil))
end
def test_compress_should_return_a_binary_string
@@ -12,4 +18,16 @@ class GzipTest < ActiveSupport::TestCase
assert_equal Encoding.find('binary'), compressed.encoding
assert !compressed.blank?, "a compressed blank string should not be blank"
end
+
+ def test_compress_should_return_gzipped_string_by_compression_level
+ source_string = "Hello World"*100
+
+ gzipped_by_speed = ActiveSupport::Gzip.compress(source_string, Zlib::BEST_SPEED)
+ assert_equal 1, Zlib::GzipReader.new(StringIO.new(gzipped_by_speed)).level
+
+ gzipped_by_best_compression = ActiveSupport::Gzip.compress(source_string, Zlib::BEST_COMPRESSION)
+ assert_equal 9, Zlib::GzipReader.new(StringIO.new(gzipped_by_best_compression)).level
+
+ assert_equal true, (gzipped_by_best_compression.bytesize < gzipped_by_speed.bytesize)
+ end
end
diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb
index ddbba444cf..5ef59b6e6b 100644
--- a/activesupport/test/i18n_test.rb
+++ b/activesupport/test/i18n_test.rb
@@ -62,7 +62,7 @@ class I18nTest < ActiveSupport::TestCase
end
def test_date_order
- assert_equal [:year, :month, :day], I18n.translate(:'date.order')
+ assert_equal %w(year month day), I18n.translate(:'date.order')
end
def test_time_am
diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb
index ca4efd2e59..7704300938 100644
--- a/activesupport/test/inflector_test_cases.rb
+++ b/activesupport/test/inflector_test_cases.rb
@@ -310,5 +310,6 @@ module InflectorTestCases
'move' => 'moves',
'cow' => 'kine',
'zombie' => 'zombies',
+ 'genus' => 'genera'
}
end
diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb
index 14ba4e0076..6aea9d56f1 100644
--- a/activesupport/test/ordered_hash_test.rb
+++ b/activesupport/test/ordered_hash_test.rb
@@ -2,6 +2,7 @@ require 'abstract_unit'
require 'active_support/json'
require 'active_support/core_ext/object/to_json'
require 'active_support/core_ext/hash/indifferent_access'
+require 'active_support/core_ext/array/extract_options'
class OrderedHashTest < ActiveSupport::TestCase
def setup
diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb
index 3f8d09c18e..e099e47e0e 100644
--- a/activesupport/test/rescuable_test.rb
+++ b/activesupport/test/rescuable_test.rb
@@ -21,7 +21,7 @@ class Stargate
rescue_from WraithAttack, :with => :sos
- rescue_from NuclearExplosion do
+ rescue_from 'NuclearExplosion' do
@result = 'alldead'
end
@@ -102,5 +102,4 @@ class RescuableTest < ActiveSupport::TestCase
result = @cool_stargate.send(:rescue_handlers).collect {|e| e.first}
assert_equal expected, result
end
-
end
diff --git a/guides/assets/images/getting_started/forbidden_attributes_for_new_post.png b/guides/assets/images/getting_started/forbidden_attributes_for_new_post.png
new file mode 100644
index 0000000000..500dfc2c02
--- /dev/null
+++ b/guides/assets/images/getting_started/forbidden_attributes_for_new_post.png
Binary files differ
diff --git a/guides/assets/images/getting_started/routing_error_no_controller.png b/guides/assets/images/getting_started/routing_error_no_controller.png
index 407ea2ea06..43ccd25252 100644
--- a/guides/assets/images/getting_started/routing_error_no_controller.png
+++ b/guides/assets/images/getting_started/routing_error_no_controller.png
Binary files differ
diff --git a/guides/assets/images/getting_started/routing_error_no_route_matches.png b/guides/assets/images/getting_started/routing_error_no_route_matches.png
index d461807c5d..1b8c0ea57e 100644
--- a/guides/assets/images/getting_started/routing_error_no_route_matches.png
+++ b/guides/assets/images/getting_started/routing_error_no_route_matches.png
Binary files differ
diff --git a/guides/assets/images/getting_started/template_is_missing_posts_new.png b/guides/assets/images/getting_started/template_is_missing_posts_new.png
index 6860aaeca7..75980432b2 100644
--- a/guides/assets/images/getting_started/template_is_missing_posts_new.png
+++ b/guides/assets/images/getting_started/template_is_missing_posts_new.png
Binary files differ
diff --git a/guides/assets/images/getting_started/unknown_action_create_for_posts.png b/guides/assets/images/getting_started/unknown_action_create_for_posts.png
index 1eca14b988..c6750e1ae1 100644
--- a/guides/assets/images/getting_started/unknown_action_create_for_posts.png
+++ b/guides/assets/images/getting_started/unknown_action_create_for_posts.png
Binary files differ
diff --git a/guides/assets/images/getting_started/unknown_action_new_for_posts.png b/guides/assets/images/getting_started/unknown_action_new_for_posts.png
index fd72586573..f4b3eff9dc 100644
--- a/guides/assets/images/getting_started/unknown_action_new_for_posts.png
+++ b/guides/assets/images/getting_started/unknown_action_new_for_posts.png
Binary files differ
diff --git a/guides/code/getting_started/.gitignore b/guides/code/getting_started/.gitignore
new file mode 100644
index 0000000000..25a742dff0
--- /dev/null
+++ b/guides/code/getting_started/.gitignore
@@ -0,0 +1,16 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+#
+# If you find yourself ignoring temporary files generated by your text editor
+# or operating system, you probably want to add a global ignore instead:
+# git config --global core.excludesfile '~/.gitignore_global'
+
+# Ignore bundler config.
+/.bundle
+
+# Ignore the default SQLite database.
+/db/*.sqlite3
+/db/*.sqlite3-journal
+
+# Ignore all logfiles and tempfiles.
+/log/*.log
+/tmp
diff --git a/guides/code/getting_started/Gemfile b/guides/code/getting_started/Gemfile
index 670a8523b0..b355c7d91a 100644
--- a/guides/code/getting_started/Gemfile
+++ b/guides/code/getting_started/Gemfile
@@ -1,38 +1,38 @@
source 'https://rubygems.org'
-gem 'rails', '3.2.3'
-
-# Bundle edge Rails instead:
-# gem 'rails', :git => 'git://github.com/rails/rails.git'
+gem 'rails', '4.0.0'
gem 'sqlite3'
-
# Gems used only for assets and not required
# in production environments by default.
group :assets do
- gem 'sass-rails', '~> 3.2.3'
- gem 'coffee-rails', '~> 3.2.1'
+ gem 'sprockets-rails'
+ gem 'sass-rails'
+ gem 'coffee-rails'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
- # gem 'therubyracer', :platform => :ruby
+ # gem 'therubyracer', platforms: :ruby
gem 'uglifier', '>= 1.0.3'
end
gem 'jquery-rails'
+# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
+gem 'turbolinks'
+
+# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
+gem 'jbuilder', '~> 1.0.1'
+
# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'
-# To use Jbuilder templates for JSON
-# gem 'jbuilder'
-
# Use unicorn as the app server
# gem 'unicorn'
# Deploy with Capistrano
-# gem 'capistrano'
+# gem 'capistrano', group: :development
# To use debugger
# gem 'debugger'
diff --git a/guides/code/getting_started/Gemfile.lock b/guides/code/getting_started/Gemfile.lock
new file mode 100644
index 0000000000..823fac5ff7
--- /dev/null
+++ b/guides/code/getting_started/Gemfile.lock
@@ -0,0 +1,150 @@
+GIT
+ remote: git://github.com/rails/activerecord-deprecated_finders.git
+ revision: 2e7b35d7948cefb2bba96438873d7f7bb1961a03
+ specs:
+ activerecord-deprecated_finders (0.0.2)
+
+GIT
+ remote: git://github.com/rails/arel.git
+ revision: 38d0a222e275d917a2c1d093b24457bafb600a00
+ specs:
+ arel (3.0.2.20120819075748)
+
+GIT
+ remote: git://github.com/rails/coffee-rails.git
+ revision: 052634e6d02d4800d7b021201cc8d5829775b3cd
+ specs:
+ coffee-rails (4.0.0.beta)
+ coffee-script (>= 2.2.0)
+ railties (>= 4.0.0.beta, < 5.0)
+
+GIT
+ remote: git://github.com/rails/sass-rails.git
+ revision: ae8138a89cac397c0df903dd533e2862902ce8f5
+ specs:
+ sass-rails (4.0.0.beta)
+ railties (>= 4.0.0.beta, < 5.0)
+ sass (>= 3.1.10)
+ sprockets-rails (~> 2.0.0.rc0)
+ tilt (~> 1.3)
+
+GIT
+ remote: git://github.com/rails/sprockets-rails.git
+ revision: 09917104fdb42245fe369612a7b0e3d77e1ba763
+ specs:
+ sprockets-rails (2.0.0.rc1)
+ actionpack (>= 3.0)
+ activesupport (>= 3.0)
+ sprockets (~> 2.8)
+
+PATH
+ remote: /Users/steve/src/rails
+ specs:
+ actionmailer (4.0.0.beta)
+ actionpack (= 4.0.0.beta)
+ mail (~> 2.5.3)
+ actionpack (4.0.0.beta)
+ activesupport (= 4.0.0.beta)
+ builder (~> 3.1.0)
+ erubis (~> 2.7.0)
+ rack (~> 1.4.3)
+ rack-test (~> 0.6.1)
+ activemodel (4.0.0.beta)
+ activesupport (= 4.0.0.beta)
+ builder (~> 3.1.0)
+ activerecord (4.0.0.beta)
+ activemodel (= 4.0.0.beta)
+ activerecord-deprecated_finders (= 0.0.2)
+ activesupport (= 4.0.0.beta)
+ arel (~> 3.0.2)
+ activesupport (4.0.0.beta)
+ i18n (~> 0.6)
+ minitest (~> 4.1)
+ multi_json (~> 1.3)
+ thread_safe (~> 0.1)
+ tzinfo (~> 0.3.33)
+ rails (4.0.0.beta)
+ actionmailer (= 4.0.0.beta)
+ actionpack (= 4.0.0.beta)
+ activerecord (= 4.0.0.beta)
+ activesupport (= 4.0.0.beta)
+ bundler (>= 1.2.2, < 2.0)
+ railties (= 4.0.0.beta)
+ sprockets-rails (~> 2.0.0.rc1)
+ railties (4.0.0.beta)
+ actionpack (= 4.0.0.beta)
+ activesupport (= 4.0.0.beta)
+ rake (>= 0.8.7)
+ rdoc (~> 3.4)
+ thor (>= 0.15.4, < 2.0)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ atomic (1.0.1)
+ builder (3.1.4)
+ coffee-script (2.2.0)
+ coffee-script-source
+ execjs
+ coffee-script-source (1.4.0)
+ erubis (2.7.0)
+ execjs (1.4.0)
+ multi_json (~> 1.0)
+ hike (1.2.1)
+ i18n (0.6.1)
+ jbuilder (1.0.2)
+ activesupport (>= 3.0.0)
+ jquery-rails (2.2.0)
+ railties (>= 3.0, < 5.0)
+ thor (>= 0.14, < 2.0)
+ json (1.7.6)
+ mail (2.5.3)
+ i18n (>= 0.4.0)
+ mime-types (~> 1.16)
+ treetop (~> 1.4.8)
+ mime-types (1.19)
+ minitest (4.4.0)
+ multi_json (1.5.0)
+ polyglot (0.3.3)
+ rack (1.4.4)
+ rack-test (0.6.2)
+ rack (>= 1.0)
+ rake (10.0.3)
+ rdoc (3.12)
+ json (~> 1.4)
+ sass (3.2.5)
+ sprockets (2.8.2)
+ hike (~> 1.2)
+ multi_json (~> 1.0)
+ rack (~> 1.0)
+ tilt (~> 1.1, != 1.3.0)
+ sqlite3 (1.3.7)
+ thor (0.16.0)
+ thread_safe (0.1.0)
+ atomic
+ tilt (1.3.3)
+ treetop (1.4.12)
+ polyglot
+ polyglot (>= 0.3.1)
+ turbolinks (1.0.0)
+ coffee-rails
+ tzinfo (0.3.35)
+ uglifier (1.3.0)
+ execjs (>= 0.3.0)
+ multi_json (~> 1.0, >= 1.0.2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ activerecord-deprecated_finders!
+ arel!
+ coffee-rails!
+ jbuilder (~> 1.0.1)
+ jquery-rails
+ rails!
+ sass-rails!
+ sprockets-rails!
+ sqlite3
+ turbolinks
+ uglifier (>= 1.0.3)
diff --git a/guides/code/getting_started/README.rdoc b/guides/code/getting_started/README.rdoc
index 232856ca5f..dd4e97e22e 100644
--- a/guides/code/getting_started/README.rdoc
+++ b/guides/code/getting_started/README.rdoc
@@ -23,6 +23,6 @@ Things you may want to cover:
* ...
-If you plan to generate application documentation with `rake doc:app` this file
-is expected to be `README.rdoc`, otherwise please feel free to rename it and use
-a different markup language. \ No newline at end of file
+
+Please feel free to use a different markup language if you do not plan to run
+<tt>rake doc:app</tt>.
diff --git a/guides/code/getting_started/Rakefile b/guides/code/getting_started/Rakefile
index e1d1ec8615..05de8bb536 100644
--- a/guides/code/getting_started/Rakefile
+++ b/guides/code/getting_started/Rakefile
@@ -1,4 +1,3 @@
-#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
diff --git a/guides/code/getting_started/app/assets/javascripts/application.js b/guides/code/getting_started/app/assets/javascripts/application.js
index 93cdae76ca..9e83eb5e7e 100644
--- a/guides/code/getting_started/app/assets/javascripts/application.js
+++ b/guides/code/getting_started/app/assets/javascripts/application.js
@@ -12,4 +12,5 @@
//
//= require jquery
//= require jquery_ujs
+//= require turbolinks
//= require_tree .
diff --git a/guides/code/getting_started/app/assets/javascripts/comments.js.coffee b/guides/code/getting_started/app/assets/javascripts/comments.js.coffee
new file mode 100644
index 0000000000..24f83d18bb
--- /dev/null
+++ b/guides/code/getting_started/app/assets/javascripts/comments.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/guides/code/getting_started/app/assets/javascripts/posts.js.coffee b/guides/code/getting_started/app/assets/javascripts/posts.js.coffee
new file mode 100644
index 0000000000..24f83d18bb
--- /dev/null
+++ b/guides/code/getting_started/app/assets/javascripts/posts.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/guides/code/getting_started/app/assets/javascripts/welcome.js.coffee b/guides/code/getting_started/app/assets/javascripts/welcome.js.coffee
new file mode 100644
index 0000000000..24f83d18bb
--- /dev/null
+++ b/guides/code/getting_started/app/assets/javascripts/welcome.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/guides/code/getting_started/app/assets/stylesheets/application.css b/guides/code/getting_started/app/assets/stylesheets/application.css
index 3b5cc6648e..3192ec897b 100644
--- a/guides/code/getting_started/app/assets/stylesheets/application.css
+++ b/guides/code/getting_started/app/assets/stylesheets/application.css
@@ -10,4 +10,4 @@
*
*= require_self
*= require_tree .
-*/
+ */
diff --git a/guides/code/getting_started/app/assets/stylesheets/comments.css.scss b/guides/code/getting_started/app/assets/stylesheets/comments.css.scss
new file mode 100644
index 0000000000..e730912783
--- /dev/null
+++ b/guides/code/getting_started/app/assets/stylesheets/comments.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the Comments controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/guides/code/getting_started/app/assets/stylesheets/posts.css.scss b/guides/code/getting_started/app/assets/stylesheets/posts.css.scss
new file mode 100644
index 0000000000..1a7e15390c
--- /dev/null
+++ b/guides/code/getting_started/app/assets/stylesheets/posts.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the posts controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/guides/code/getting_started/app/assets/stylesheets/welcome.css.scss b/guides/code/getting_started/app/assets/stylesheets/welcome.css.scss
new file mode 100644
index 0000000000..77ce11a740
--- /dev/null
+++ b/guides/code/getting_started/app/assets/stylesheets/welcome.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the welcome controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/guides/code/getting_started/app/controllers/application_controller.rb b/guides/code/getting_started/app/controllers/application_controller.rb
index e8065d9505..d83690e1b9 100644
--- a/guides/code/getting_started/app/controllers/application_controller.rb
+++ b/guides/code/getting_started/app/controllers/application_controller.rb
@@ -1,3 +1,5 @@
class ApplicationController < ActionController::Base
- protect_from_forgery
+ # Prevent CSRF attacks by raising an exception.
+ # For APIs, you may want to use :null_session instead.
+ protect_from_forgery with: :exception
end
diff --git a/guides/code/getting_started/app/controllers/comments_controller.rb b/guides/code/getting_started/app/controllers/comments_controller.rb
index cf3d1be42e..0082e9c8ec 100644
--- a/guides/code/getting_started/app/controllers/comments_controller.rb
+++ b/guides/code/getting_started/app/controllers/comments_controller.rb
@@ -1,9 +1,10 @@
class CommentsController < ApplicationController
- http_basic_authenticate_with :name => "dhh", :password => "secret", :only => :destroy
+  http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy
+
def create
@post = Post.find(params[:post_id])
- @comment = @post.comments.create(params[:comment])
+ @comment = @post.comments.create(params[:comment].permit(:commenter, :body))
redirect_to post_path(@post)
end
@@ -13,5 +14,4 @@ class CommentsController < ApplicationController
@comment.destroy
redirect_to post_path(@post)
end
-
end
diff --git a/guides/code/getting_started/app/mailers/.gitkeep b/guides/code/getting_started/app/controllers/concerns/.keep
index e69de29bb2..e69de29bb2 100644
--- a/guides/code/getting_started/app/mailers/.gitkeep
+++ b/guides/code/getting_started/app/controllers/concerns/.keep
diff --git a/guides/code/getting_started/app/controllers/posts_controller.rb b/guides/code/getting_started/app/controllers/posts_controller.rb
index b74c66ef13..0398395200 100644
--- a/guides/code/getting_started/app/controllers/posts_controller.rb
+++ b/guides/code/getting_started/app/controllers/posts_controller.rb
@@ -1,7 +1,7 @@
class PostsController < ApplicationController
- http_basic_authenticate_with :name => "dhh", :password => "secret", :except => [:index, :show]
-
+  http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]
+
def index
@posts = Post.all
end
@@ -10,31 +10,31 @@ class PostsController < ApplicationController
@post = Post.find(params[:id])
end
- def new
- @post = Post.new
+ def edit
+ @post = Post.find(params[:id])
end
- def create
- @post = Post.new(params[:post])
+ def update
+ @post = Post.find(params[:id])
- if @post.save
- redirect_to :action => :show, :id => @post.id
+ if @post.update(params[:post].permit(:title, :text))
+ redirect_to action: :show, id: @post.id
else
- render 'new'
+ render 'edit'
end
end
- def edit
- @post = Post.find(params[:id])
+ def new
+ @post = Post.new
end
- def update
- @post = Post.find(params[:id])
+ def create
+ @post = Post.new(params[:post].permit(:title, :text))
- if @post.update(params[:post])
- redirect_to :action => :show, :id => @post.id
+ if @post.save
+ redirect_to action: :show, id: @post.id
else
- render 'edit'
+ render 'new'
end
end
@@ -42,6 +42,6 @@ class PostsController < ApplicationController
@post = Post.find(params[:id])
@post.destroy
- redirect_to :action => :index
+ redirect_to action: :index
end
end
diff --git a/guides/code/getting_started/app/models/.gitkeep b/guides/code/getting_started/app/mailers/.keep
index e69de29bb2..e69de29bb2 100644
--- a/guides/code/getting_started/app/models/.gitkeep
+++ b/guides/code/getting_started/app/mailers/.keep
diff --git a/guides/code/getting_started/lib/assets/.gitkeep b/guides/code/getting_started/app/models/.keep
index e69de29bb2..e69de29bb2 100644
--- a/guides/code/getting_started/lib/assets/.gitkeep
+++ b/guides/code/getting_started/app/models/.keep
diff --git a/guides/code/getting_started/lib/tasks/.gitkeep b/guides/code/getting_started/app/models/concerns/.keep
index e69de29bb2..e69de29bb2 100644
--- a/guides/code/getting_started/lib/tasks/.gitkeep
+++ b/guides/code/getting_started/app/models/concerns/.keep
diff --git a/guides/code/getting_started/app/models/post.rb b/guides/code/getting_started/app/models/post.rb
index 21387340b0..64e0d721fd 100644
--- a/guides/code/getting_started/app/models/post.rb
+++ b/guides/code/getting_started/app/models/post.rb
@@ -1,6 +1,7 @@
class Post < ActiveRecord::Base
- validates :title, :presence => true,
- :length => { :minimum => 5 }
-
- has_many :comments, :dependent => :destroy
+ has_many :comments, dependent: :destroy
+
+ validates :title,
+ presence: true,
+ length: { minimum: 5 }
end
diff --git a/guides/code/getting_started/app/views/comments/_comment.html.erb b/guides/code/getting_started/app/views/comments/_comment.html.erb
index 3d2bc1590e..593493339e 100644
--- a/guides/code/getting_started/app/views/comments/_comment.html.erb
+++ b/guides/code/getting_started/app/views/comments/_comment.html.erb
@@ -2,7 +2,7 @@
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
-
+
<p>
<strong>Comment:</strong>
<%= comment.body %>
@@ -10,6 +10,6 @@
<p>
<%= link_to 'Destroy Comment', [comment.post, comment],
- :method => :delete,
- :data => { :confirm => 'Are you sure?' } %>
+ method: :delete,
+ data: { confirm: 'Are you sure?' } %>
</p>
diff --git a/guides/code/getting_started/app/views/layouts/application.html.erb b/guides/code/getting_started/app/views/layouts/application.html.erb
index 6578a41da2..95368c37a3 100644
--- a/guides/code/getting_started/app/views/layouts/application.html.erb
+++ b/guides/code/getting_started/app/views/layouts/application.html.erb
@@ -2,8 +2,8 @@
<html>
<head>
<title>Blog</title>
- <%= stylesheet_link_tag "application", :media => "all" %>
- <%= javascript_include_tag "application" %>
+ <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
+ <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
<%= csrf_meta_tags %>
</head>
<body>
diff --git a/guides/code/getting_started/app/views/posts/_form.html.erb b/guides/code/getting_started/app/views/posts/_form.html.erb
index f22139938c..c9fb74af9c 100644
--- a/guides/code/getting_started/app/views/posts/_form.html.erb
+++ b/guides/code/getting_started/app/views/posts/_form.html.erb
@@ -1,25 +1,27 @@
<%= form_for @post do |f| %>
<% if @post.errors.any? %>
- <div id="errorExplanation">
- <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>
- <ul>
- <% @post.errors.full_messages.each do |msg| %>
- <li><%= msg %></li>
- <% end %>
- </ul>
- </div>
+ <div id="errorExplanation">
+ <h2><%= pluralize(@post.errors.count, "error") %> prohibited
+ this post from being saved:</h2>
+ <ul>
+ <% @post.errors.full_messages.each do |msg| %>
+ <li><%= msg %></li>
+ <% end %>
+ </ul>
+ </div>
<% end %>
<p>
- <%= f.label :title %><br />
+ <%= f.label :title %><br>
<%= f.text_field :title %>
</p>
-
+
<p>
- <%= f.label :text %><br />
+ <%= f.label :text %><br>
<%= f.text_area :text %>
</p>
-
+
<p>
<%= f.submit %>
</p>
<% end %>
+
diff --git a/guides/code/getting_started/app/views/posts/edit.html.erb b/guides/code/getting_started/app/views/posts/edit.html.erb
index 911a48569d..393e7430d0 100644
--- a/guides/code/getting_started/app/views/posts/edit.html.erb
+++ b/guides/code/getting_started/app/views/posts/edit.html.erb
@@ -1,5 +1,5 @@
-<h1>Editing post</h1>
-
+<h1>Edit post</h1>
+
<%= render 'form' %>
-
-<%= link_to 'Back', :action => :index %>
+
+<%= link_to 'Back', action: :index %>
diff --git a/guides/code/getting_started/app/views/posts/index.html.erb b/guides/code/getting_started/app/views/posts/index.html.erb
index 9a0e90eadc..7369f0396f 100644
--- a/guides/code/getting_started/app/views/posts/index.html.erb
+++ b/guides/code/getting_started/app/views/posts/index.html.erb
@@ -1,7 +1,4 @@
-<h1>Listing posts</h1>
-
-<%= link_to 'New post', :action => :new %>
-
+<h1>Listing Posts</h1>
<table>
<tr>
<th>Title</th>
@@ -10,14 +7,15 @@
<th></th>
<th></th>
</tr>
-
+
<% @posts.each do |post| %>
<tr>
<td><%= post.title %></td>
<td><%= post.text %></td>
- <td><%= link_to 'Show', :action => :show, :id => post.id %>
- <td><%= link_to 'Edit', :action => :edit, :id => post.id %>
- <td><%= link_to 'Destroy', { :action => :destroy, :id => post.id }, :method => :delete, :data => { :confirm => 'Are you sure?' } %>
+ <td><%= link_to 'Show', action: :show, id: post.id %></td>
+ <td><%= link_to 'Edit', action: :edit, id: post.id %></td>
+ <td><%= link_to 'Destroy', { action: :destroy, id: post.id },
+ method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
diff --git a/guides/code/getting_started/app/views/posts/new.html.erb b/guides/code/getting_started/app/views/posts/new.html.erb
index ce9523a721..efa81038ec 100644
--- a/guides/code/getting_started/app/views/posts/new.html.erb
+++ b/guides/code/getting_started/app/views/posts/new.html.erb
@@ -1,5 +1,5 @@
<h1>New post</h1>
-
+
<%= render 'form' %>
-
-<%= link_to 'Back', :action => :index %>
+
+<%= link_to 'Back', action: :index %>
diff --git a/guides/code/getting_started/app/views/posts/show.html.erb b/guides/code/getting_started/app/views/posts/show.html.erb
index 65809033ed..e99e9edbb3 100644
--- a/guides/code/getting_started/app/views/posts/show.html.erb
+++ b/guides/code/getting_started/app/views/posts/show.html.erb
@@ -2,7 +2,7 @@
<strong>Title:</strong>
<%= @post.title %>
</p>
-
+
<p>
<strong>Text:</strong>
<%= @post.text %>
@@ -10,9 +10,9 @@
<h2>Comments</h2>
<%= render @post.comments %>
-
+
<h2>Add a comment:</h2>
<%= render "comments/form" %>
-
+
<%= link_to 'Edit Post', edit_post_path(@post) %> |
<%= link_to 'Back to Posts', posts_path %>
diff --git a/guides/code/getting_started/app/views/welcome/index.html.erb b/guides/code/getting_started/app/views/welcome/index.html.erb
index e04680ea7e..738e12d7dc 100644
--- a/guides/code/getting_started/app/views/welcome/index.html.erb
+++ b/guides/code/getting_started/app/views/welcome/index.html.erb
@@ -1,2 +1,3 @@
<h1>Hello, Rails!</h1>
-<%= link_to "My Blog", :controller => "posts" %>
+
+<%= link_to "My Blog", controller: "posts" %>
diff --git a/guides/code/getting_started/bin/bundle b/guides/code/getting_started/bin/bundle
new file mode 100755
index 0000000000..45cf37fba4
--- /dev/null
+++ b/guides/code/getting_started/bin/bundle
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+require 'rubygems'
+load Gem.bin_path('bundler', 'bundle')
diff --git a/guides/code/getting_started/bin/rails b/guides/code/getting_started/bin/rails
new file mode 100755
index 0000000000..728cd85aa5
--- /dev/null
+++ b/guides/code/getting_started/bin/rails
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+APP_PATH = File.expand_path('../../config/application', __FILE__)
+require_relative '../config/boot'
+require 'rails/commands'
diff --git a/guides/code/getting_started/bin/rake b/guides/code/getting_started/bin/rake
new file mode 100755
index 0000000000..17240489f6
--- /dev/null
+++ b/guides/code/getting_started/bin/rake
@@ -0,0 +1,4 @@
+#!/usr/bin/env ruby
+require_relative '../config/boot'
+require 'rake'
+Rake.application.run
diff --git a/guides/code/getting_started/config/application.rb b/guides/code/getting_started/config/application.rb
index d53c9fd8bc..526a782b5c 100644
--- a/guides/code/getting_started/config/application.rb
+++ b/guides/code/getting_started/config/application.rb
@@ -2,12 +2,8 @@ require File.expand_path('../boot', __FILE__)
require 'rails/all'
-if defined?(Bundler)
- # If you precompile assets before deploying to production, use this line
- Bundler.require(*Rails.groups(:assets => %w(development test)))
- # If you want your assets lazily compiled in production, use this line
- # Bundler.require(:default, :assets, Rails.env)
-end
+# Assets should be precompiled for production (so we don't need the gems loaded then)
+Bundler.require(*Rails.groups(assets: %w(development test)))
module Blog
class Application < Rails::Application
@@ -17,36 +13,5 @@ module Blog
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
-
- # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
- # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
- # config.time_zone = 'Central Time (US & Canada)'
-
- # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
- # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
- # config.i18n.default_locale = :de
-
- # Configure the default encoding used in templates for Ruby 1.9.
- config.encoding = "utf-8"
-
- # Configure sensitive parameters which will be filtered from the log file.
- config.filter_parameters += [:password]
-
- # Use SQL instead of Active Record's schema dumper when creating the database.
- # This is necessary if your schema can't be completely dumped by the schema dumper,
- # like if you have constraints or database-specific column types.
- # config.active_record.schema_format = :sql
-
- # Enforce whitelist mode for mass assignment.
- # This will create an empty whitelist of attributes available for mass-assignment for all models
- # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
- # parameters by using an attr_accessible or attr_protected declaration.
- # config.active_record.whitelist_attributes = true
-
- # Enable the asset pipeline.
- config.assets.enabled = true
-
- # Version of your assets, change this if you want to expire all your assets.
- config.assets.version = '1.0'
end
end
diff --git a/guides/code/getting_started/config/environment.rb b/guides/code/getting_started/config/environment.rb
index 8f728b7ce7..2d65111004 100644
--- a/guides/code/getting_started/config/environment.rb
+++ b/guides/code/getting_started/config/environment.rb
@@ -1,5 +1,5 @@
-# Load the rails application
+# Load the rails application.
require File.expand_path('../application', __FILE__)
-# Initialize the rails application
+# Initialize the rails application.
Blog::Application.initialize!
diff --git a/guides/code/getting_started/config/environments/development.rb b/guides/code/getting_started/config/environments/development.rb
index cec2b20c0b..ed2667ac58 100644
--- a/guides/code/getting_started/config/environments/development.rb
+++ b/guides/code/getting_started/config/environments/development.rb
@@ -2,10 +2,13 @@ Blog::Application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded on
- # every request. This slows down response time but is perfect for development
+ # every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
+ # Do not eager load code on boot.
+ config.eager_load = false
+
# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
@@ -19,16 +22,13 @@ Blog::Application.configure do
# Only use best-standards-support built into browsers.
config.action_dispatch.best_standards_support = :builtin
- # Raise exception on mass assignment protection for ActiveRecord models.
- config.active_record.mass_assignment_sanitizer = :strict
-
# Log the query plan for queries taking more than this (works
# with SQLite, MySQL, and PostgreSQL).
config.active_record.auto_explain_threshold_in_seconds = 0.5
- # Do not compress assets.
- config.assets.compress = false
+ # Raise an error on page load if there are pending migrations
+ config.active_record.migration_error = :page_load
- # Expands the lines which load the assets.
+ # Debug mode disables concatenation and preprocessing of assets.
config.assets.debug = true
end
diff --git a/guides/code/getting_started/config/environments/production.rb b/guides/code/getting_started/config/environments/production.rb
index ecc35b030b..58dab2a319 100644
--- a/guides/code/getting_started/config/environments/production.rb
+++ b/guides/code/getting_started/config/environments/production.rb
@@ -4,24 +4,36 @@ Blog::Application.configure do
# Code is not reloaded between requests.
config.cache_classes = true
+ # Eager load code on boot. This eager loads most of Rails and
+ # your application in memory, allowing both thread web servers
+ # and those relying on copy on write to perform better.
+ # Rake tasks automatically ignore this option for performance.
+ config.eager_load = true
+
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
+ # Enable Rack::Cache to put a simple HTTP cache in front of your application
+ # Add `rack-cache` to your Gemfile before enabling this.
+ # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
+ # config.action_dispatch.rack_cache = true
+
# Disable Rails's static asset server (Apache or nginx will already do this).
config.serve_static_assets = false
# Compress JavaScripts and CSS.
- config.assets.compress = true
+ config.assets.js_compressor = :uglifier
+ # config.assets.css_compressor = :sass
- # Don't fallback to assets pipeline if a precompiled asset is missed.
+ # Whether to fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = false
# Generate digests for assets URLs.
config.assets.digest = true
- # Defaults to nil
- # config.assets.manifest = YOUR_PATH
+ # Version of your assets, change this if you want to expire all your assets.
+ config.assets.version = '1.0'
# Specifies the header that your server uses for sending files.
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
@@ -30,8 +42,8 @@ Blog::Application.configure do
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
- # See everything in the log (default is :info).
- # config.log_level = :debug
+ # Set to :debug to see everything in the log.
+ config.log_level = :info
# Prepend all log lines with the following tags.
# config.log_tags = [ :subdomain, :uuid ]
@@ -45,15 +57,14 @@ Blog::Application.configure do
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = "http://assets.example.com"
- # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added).
+ # Precompile additional assets.
+ # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
# config.assets.precompile += %w( search.js )
- # Disable delivery errors, bad email addresses will be ignored.
+ # Ignore bad email addresses and do not raise email delivery errors.
+ # Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
- # Enable threaded mode.
- # config.threadsafe!
-
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation can not be found).
config.i18n.fallbacks = true
@@ -64,4 +75,10 @@ Blog::Application.configure do
# Log the query plan for queries taking more than this (works
# with SQLite, MySQL, and PostgreSQL).
# config.active_record.auto_explain_threshold_in_seconds = 0.5
+
+ # Disable automatic flushing of the log to improve performance.
+ # config.autoflush_log = false
+
+ # Use default logging formatter so that PID and timestamp are not suppressed.
+ config.log_formatter = ::Logger::Formatter.new
end
diff --git a/guides/code/getting_started/config/environments/test.rb b/guides/code/getting_started/config/environments/test.rb
index f2bc932fb3..00adaa5015 100644
--- a/guides/code/getting_started/config/environments/test.rb
+++ b/guides/code/getting_started/config/environments/test.rb
@@ -2,11 +2,16 @@ Blog::Application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# The test environment is used exclusively to run your application's
- # test suite. You never need to work with it otherwise. Remember that
+ # test suite. You never need to work with it otherwise. Remember that
# your test database is "scratch space" for the test suite and is wiped
- # and recreated between test runs. Don't rely on the data there!
+ # and recreated between test runs. Don't rely on the data there!
config.cache_classes = true
+ # Do not eager load code on boot. This avoids loading your whole application
+ # just for the purpose of running a single test. If you are using a tool that
+ # preloads Rails for running tests, you may have to set it to true.
+ config.eager_load = false
+
# Configure static asset server for tests with Cache-Control for performance.
config.serve_static_assets = true
config.static_cache_control = "public, max-age=3600"
@@ -26,9 +31,6 @@ Blog::Application.configure do
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
- # Raise exception on mass assignment protection for Active Record models.
- config.active_record.mass_assignment_sanitizer = :strict
-
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
end
diff --git a/guides/code/getting_started/config/initializers/filter_parameter_logging.rb b/guides/code/getting_started/config/initializers/filter_parameter_logging.rb
new file mode 100644
index 0000000000..4a994e1e7b
--- /dev/null
+++ b/guides/code/getting_started/config/initializers/filter_parameter_logging.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Configure sensitive parameters which will be filtered from the log file.
+Rails.application.config.filter_parameters += [:password]
diff --git a/guides/code/getting_started/config/initializers/inflections.rb b/guides/code/getting_started/config/initializers/inflections.rb
index 5d8d9be237..ac033bf9dc 100644
--- a/guides/code/getting_started/config/initializers/inflections.rb
+++ b/guides/code/getting_started/config/initializers/inflections.rb
@@ -1,15 +1,16 @@
# Be sure to restart your server when you modify this file.
-# Add new inflection rules using the following format
-# (all these examples are active by default):
-# ActiveSupport::Inflector.inflections do |inflect|
+# Add new inflection rules using the following format. Inflections
+# are locale specific, and you may define rules for as many different
+# locales as you wish. All of these examples are active by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
# inflect.singular /^(ox)en/i, '\1'
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
-#
+
# These inflection rules are supported but not enabled by default:
-# ActiveSupport::Inflector.inflections do |inflect|
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.acronym 'RESTful'
# end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb b/guides/code/getting_started/config/initializers/locale.rb
index d89dac7c6a..d89dac7c6a 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb
+++ b/guides/code/getting_started/config/initializers/locale.rb
diff --git a/guides/code/getting_started/config/initializers/secret_token.rb b/guides/code/getting_started/config/initializers/secret_token.rb
index 969ecaad65..aaf57731be 100644
--- a/guides/code/getting_started/config/initializers/secret_token.rb
+++ b/guides/code/getting_started/config/initializers/secret_token.rb
@@ -2,8 +2,11 @@
# Your secret key for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
+
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
-# Make sure your secret key is kept private
+# You can use `rake secret` to generate a secure secret key.
+
+# Make sure your secret_key_base is kept private
# if you're sharing your code publicly.
-Blog::Application.config.secret_key_base = '685a9bf865b728c6549a191c90851c1b5ec41ecb60b9e94ad79dd3f824749798aa7b5e94431901960bee57809db0947b481570f7f13376b7ca190fa28099c459'
+Blog::Application.config.secret_key_base = 'e8aab50cec8a06a75694111a4cbaf6e22fc288ccbc6b268683aae7273043c69b15ca07d10c92a788dd6077a54762cbfcc55f19c3459f7531221b3169f8171a53'
diff --git a/guides/code/getting_started/config/initializers/session_store.rb b/guides/code/getting_started/config/initializers/session_store.rb
index 3b2ca93ab9..2e37d93799 100644
--- a/guides/code/getting_started/config/initializers/session_store.rb
+++ b/guides/code/getting_started/config/initializers/session_store.rb
@@ -1,3 +1,3 @@
# Be sure to restart your server when you modify this file.
-Blog::Application.config.session_store :cookie_store, key: '_blog_session'
+Blog::Application.config.session_store :encrypted_cookie_store, key: '_blog_session'
diff --git a/guides/code/getting_started/config/initializers/wrap_parameters.rb b/guides/code/getting_started/config/initializers/wrap_parameters.rb
index 999df20181..33725e95fd 100644
--- a/guides/code/getting_started/config/initializers/wrap_parameters.rb
+++ b/guides/code/getting_started/config/initializers/wrap_parameters.rb
@@ -1,14 +1,14 @@
# Be sure to restart your server when you modify this file.
-#
+
# This file contains settings for ActionController::ParamsWrapper which
# is enabled by default.
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
- wrap_parameters format: [:json]
+ wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
end
-# Disable root element in JSON by default.
-ActiveSupport.on_load(:active_record) do
- self.include_root_in_json = false
-end
+# To enable root element in JSON for ActiveRecord objects.
+# ActiveSupport.on_load(:active_record) do
+# self.include_root_in_json = true
+# end
diff --git a/guides/code/getting_started/config/locales/en.yml b/guides/code/getting_started/config/locales/en.yml
index 179c14ca52..0653957166 100644
--- a/guides/code/getting_started/config/locales/en.yml
+++ b/guides/code/getting_started/config/locales/en.yml
@@ -1,5 +1,23 @@
-# Sample localization file for English. Add more files in this directory for other locales.
-# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
+# Files in the config/locales directory are used for internationalization
+# and are automatically loaded by Rails. If you want to use locales other
+# than English, add the necessary files in this directory.
+#
+# To use the locales, use `I18n.t`:
+#
+# I18n.t 'hello'
+#
+# In views, this is aliased to just `t`:
+#
+# <%= t('hello') %>
+#
+# To use a different locale, set it with `I18n.locale`:
+#
+# I18n.locale = :es
+#
+# This would use the information in config/locales/es.yml.
+#
+# To learn more, please read the Rails Internationalization guide
+# available at http://guides.rubyonrails.org/i18n.html.
en:
hello: "Hello world"
diff --git a/guides/code/getting_started/config/routes.rb b/guides/code/getting_started/config/routes.rb
index d94b0d6f33..9950568629 100644
--- a/guides/code/getting_started/config/routes.rb
+++ b/guides/code/getting_started/config/routes.rb
@@ -1,63 +1,7 @@
Blog::Application.routes.draw do
-
resources :posts do
resources :comments
end
-
- # The priority is based upon order of creation:
- # first created -> highest priority.
-
- # Sample of regular route:
- # match 'products/:id' => 'catalog#view'
- # Keep in mind you can assign values other than :controller and :action
-
- # Sample of named route:
- # match 'products/:id/purchase' => 'catalog#purchase', as: :purchase
- # This route can be invoked with purchase_url(id: product.id)
-
- # Sample resource route (maps HTTP verbs to controller actions automatically):
- # resources :products
-
- # Sample resource route with options:
- # resources :products do
- # member do
- # get 'short'
- # post 'toggle'
- # end
- #
- # collection do
- # get 'sold'
- # end
- # end
-
- # Sample resource route with sub-resources:
- # resources :products do
- # resources :comments, :sales
- # resource :seller
- # end
-
- # Sample resource route with more complex sub-resources
- # resources :products do
- # resources :comments
- # resources :sales do
- # get 'recent', on: :collection
- # end
- # end
-
- # Sample resource route within a namespace:
- # namespace :admin do
- # # Directs /admin/products/* to Admin::ProductsController
- # # (app/controllers/admin/products_controller.rb)
- # resources :products
- # end
-
- # You can have the root of your site routed with "root"
- # just remember to delete public/index.html.
- root :to => "welcome#index"
-
- # See how all your routes lay out with "rake routes"
-
- # This is a legacy wild controller route that's not recommended for RESTful applications.
- # Note: This route will make all actions in every controller accessible via GET requests.
- # match ':controller(/:action(/:id))(.:format)'
+
+ root to: "welcome#index"
end
diff --git a/guides/code/getting_started/db/migrate/20120420083127_create_posts.rb b/guides/code/getting_started/db/migrate/20130122042648_create_posts.rb
index 602bef31ab..602bef31ab 100644
--- a/guides/code/getting_started/db/migrate/20120420083127_create_posts.rb
+++ b/guides/code/getting_started/db/migrate/20130122042648_create_posts.rb
diff --git a/guides/code/getting_started/db/migrate/20110901012815_create_comments.rb b/guides/code/getting_started/db/migrate/20130122045842_create_comments.rb
index adda8078c1..3e51f9c0f7 100644
--- a/guides/code/getting_started/db/migrate/20110901012815_create_comments.rb
+++ b/guides/code/getting_started/db/migrate/20130122045842_create_comments.rb
@@ -3,10 +3,9 @@ class CreateComments < ActiveRecord::Migration
create_table :comments do |t|
t.string :commenter
t.text :body
- t.references :post
+ t.references :post, index: true
t.timestamps
end
- add_index :comments, :post_id
end
end
diff --git a/guides/code/getting_started/db/schema.rb b/guides/code/getting_started/db/schema.rb
index cfb56ca9b9..101fe712a1 100644
--- a/guides/code/getting_started/db/schema.rb
+++ b/guides/code/getting_started/db/schema.rb
@@ -9,34 +9,25 @@
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
-# It's strongly recommended to check this file into your version control system.
+# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20120420083127) do
+ActiveRecord::Schema.define(version: 20130122045842) do
- create_table "comments", :force => true do |t|
+ create_table "comments", force: true do |t|
t.string "commenter"
t.text "body"
t.integer "post_id"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at"
+ t.datetime "updated_at"
end
- add_index "comments", ["post_id"], :name => "index_comments_on_post_id"
+ add_index "comments", ["post_id"], name: "index_comments_on_post_id"
- create_table "posts", :force => true do |t|
+ create_table "posts", force: true do |t|
t.string "title"
t.text "text"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
+ t.datetime "created_at"
+ t.datetime "updated_at"
end
- create_table "tags", :force => true do |t|
- t.string "name"
- t.integer "post_id"
- t.datetime "created_at", :null => false
- t.datetime "updated_at", :null => false
- end
-
- add_index "tags", ["post_id"], :name => "index_tags_on_post_id"
-
end
diff --git a/guides/code/getting_started/test/fixtures/.gitkeep b/guides/code/getting_started/lib/assets/.keep
index e69de29bb2..e69de29bb2 100644
--- a/guides/code/getting_started/test/fixtures/.gitkeep
+++ b/guides/code/getting_started/lib/assets/.keep
diff --git a/guides/code/getting_started/test/functional/.gitkeep b/guides/code/getting_started/lib/tasks/.keep
index e69de29bb2..e69de29bb2 100644
--- a/guides/code/getting_started/test/functional/.gitkeep
+++ b/guides/code/getting_started/lib/tasks/.keep
diff --git a/guides/code/getting_started/test/integration/.gitkeep b/guides/code/getting_started/log/.keep
index e69de29bb2..e69de29bb2 100644
--- a/guides/code/getting_started/test/integration/.gitkeep
+++ b/guides/code/getting_started/log/.keep
diff --git a/guides/code/getting_started/public/404.html b/guides/code/getting_started/public/404.html
index 9a48320a5f..3d875c342e 100644
--- a/guides/code/getting_started/public/404.html
+++ b/guides/code/getting_started/public/404.html
@@ -2,7 +2,7 @@
<html>
<head>
<title>The page you were looking for doesn't exist (404)</title>
- <style type="text/css">
+ <style>
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
@@ -22,5 +22,6 @@
<h1>The page you were looking for doesn't exist.</h1>
<p>You may have mistyped the address or the page may have moved.</p>
</div>
+ <p>If you are the application owner check the logs for more information.</p>
</body>
</html>
diff --git a/guides/code/getting_started/public/422.html b/guides/code/getting_started/public/422.html
index 83660ab187..3f1bfb3417 100644
--- a/guides/code/getting_started/public/422.html
+++ b/guides/code/getting_started/public/422.html
@@ -2,7 +2,7 @@
<html>
<head>
<title>The change you wanted was rejected (422)</title>
- <style type="text/css">
+ <style>
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
diff --git a/guides/code/getting_started/public/500.html b/guides/code/getting_started/public/500.html
index f3648a0dbc..012977d3d2 100644
--- a/guides/code/getting_started/public/500.html
+++ b/guides/code/getting_started/public/500.html
@@ -2,7 +2,7 @@
<html>
<head>
<title>We're sorry, but something went wrong (500)</title>
- <style type="text/css">
+ <style>
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
@@ -21,5 +21,6 @@
<div class="dialog">
<h1>We're sorry, but something went wrong.</h1>
</div>
+ <p>If you are the application owner check the logs for more information.</p>
</body>
</html>
diff --git a/guides/code/getting_started/script/rails b/guides/code/getting_started/script/rails
deleted file mode 100755
index f8da2cffd4..0000000000
--- a/guides/code/getting_started/script/rails
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env ruby
-# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
-
-APP_PATH = File.expand_path('../../config/application', __FILE__)
-require File.expand_path('../../config/boot', __FILE__)
-require 'rails/commands'
diff --git a/guides/code/getting_started/test/unit/.gitkeep b/guides/code/getting_started/test/controllers/.keep
index e69de29bb2..e69de29bb2 100644
--- a/guides/code/getting_started/test/unit/.gitkeep
+++ b/guides/code/getting_started/test/controllers/.keep
diff --git a/guides/code/getting_started/test/functional/comments_controller_test.rb b/guides/code/getting_started/test/controllers/comments_controller_test.rb
index 2ec71b4ec5..2ec71b4ec5 100644
--- a/guides/code/getting_started/test/functional/comments_controller_test.rb
+++ b/guides/code/getting_started/test/controllers/comments_controller_test.rb
diff --git a/guides/code/getting_started/test/unit/tag_test.rb b/guides/code/getting_started/test/controllers/posts_controller_test.rb
index b8498a117c..7a6ee4f1db 100644
--- a/guides/code/getting_started/test/unit/tag_test.rb
+++ b/guides/code/getting_started/test/controllers/posts_controller_test.rb
@@ -1,6 +1,6 @@
require 'test_helper'
-class TagTest < ActiveSupport::TestCase
+class PostsControllerTest < ActionController::TestCase
# test "the truth" do
# assert true
# end
diff --git a/guides/code/getting_started/test/functional/welcome_controller_test.rb b/guides/code/getting_started/test/controllers/welcome_controller_test.rb
index e4d5abae11..dff8e9d2c5 100644
--- a/guides/code/getting_started/test/functional/welcome_controller_test.rb
+++ b/guides/code/getting_started/test/controllers/welcome_controller_test.rb
@@ -5,4 +5,5 @@ class WelcomeControllerTest < ActionController::TestCase
get :index
assert_response :success
end
+
end
diff --git a/guides/code/getting_started/vendor/plugins/.gitkeep b/guides/code/getting_started/test/fixtures/.keep
index e69de29bb2..e69de29bb2 100644
--- a/guides/code/getting_started/vendor/plugins/.gitkeep
+++ b/guides/code/getting_started/test/fixtures/.keep
diff --git a/guides/code/getting_started/test/fixtures/comments.yml b/guides/code/getting_started/test/fixtures/comments.yml
index d33da386bf..0cd36069e4 100644
--- a/guides/code/getting_started/test/fixtures/comments.yml
+++ b/guides/code/getting_started/test/fixtures/comments.yml
@@ -1,11 +1,11 @@
-# Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
one:
commenter: MyString
body: MyText
- post:
+ post_id:
two:
commenter: MyString
body: MyText
- post:
+ post_id:
diff --git a/guides/code/getting_started/test/fixtures/posts.yml b/guides/code/getting_started/test/fixtures/posts.yml
index e1edfd385e..617a24b858 100644
--- a/guides/code/getting_started/test/fixtures/posts.yml
+++ b/guides/code/getting_started/test/fixtures/posts.yml
@@ -1,4 +1,4 @@
-# Read about fixtures at http://api.rubyonrails.org/classes/Fixtures.html
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
one:
title: MyString
diff --git a/guides/code/getting_started/test/functional/posts_controller_test.rb b/guides/code/getting_started/test/functional/posts_controller_test.rb
deleted file mode 100644
index b8f7b07820..0000000000
--- a/guides/code/getting_started/test/functional/posts_controller_test.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require 'test_helper'
-
-class PostsControllerTest < ActionController::TestCase
- setup do
- @post = posts(:one)
- end
-
- test "should get index" do
- get :index
- assert_response :success
- assert_not_nil assigns(:posts)
- end
-
- test "should get new" do
- get :new
- assert_response :success
- end
-
- test "should create post" do
- assert_difference('Post.count') do
- post :create, post: @post.attributes
- end
-
- assert_redirected_to post_path(assigns(:post))
- end
-
- test "should show post" do
- get :show, id: @post.to_param
- assert_response :success
- end
-
- test "should get edit" do
- get :edit, id: @post.to_param
- assert_response :success
- end
-
- test "should update post" do
- put :update, id: @post.to_param, post: @post.attributes
- assert_redirected_to post_path(assigns(:post))
- end
-
- test "should destroy post" do
- assert_difference('Post.count', -1) do
- delete :destroy, id: @post.to_param
- end
-
- assert_redirected_to posts_path
- end
-end
diff --git a/guides/code/getting_started/test/helpers/.keep b/guides/code/getting_started/test/helpers/.keep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/guides/code/getting_started/test/helpers/.keep
diff --git a/guides/code/getting_started/test/unit/helpers/comments_helper_test.rb b/guides/code/getting_started/test/helpers/comments_helper_test.rb
index 2518c16bd5..2518c16bd5 100644
--- a/guides/code/getting_started/test/unit/helpers/comments_helper_test.rb
+++ b/guides/code/getting_started/test/helpers/comments_helper_test.rb
diff --git a/guides/code/getting_started/test/unit/helpers/posts_helper_test.rb b/guides/code/getting_started/test/helpers/posts_helper_test.rb
index 48549c2ea1..48549c2ea1 100644
--- a/guides/code/getting_started/test/unit/helpers/posts_helper_test.rb
+++ b/guides/code/getting_started/test/helpers/posts_helper_test.rb
diff --git a/guides/code/getting_started/test/helpers/welcome_helper_test.rb b/guides/code/getting_started/test/helpers/welcome_helper_test.rb
new file mode 100644
index 0000000000..d6ded5995f
--- /dev/null
+++ b/guides/code/getting_started/test/helpers/welcome_helper_test.rb
@@ -0,0 +1,4 @@
+require 'test_helper'
+
+class WelcomeHelperTest < ActionView::TestCase
+end
diff --git a/guides/code/getting_started/test/integration/.keep b/guides/code/getting_started/test/integration/.keep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/guides/code/getting_started/test/integration/.keep
diff --git a/guides/code/getting_started/test/mailers/.keep b/guides/code/getting_started/test/mailers/.keep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/guides/code/getting_started/test/mailers/.keep
diff --git a/guides/code/getting_started/test/models/.keep b/guides/code/getting_started/test/models/.keep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/guides/code/getting_started/test/models/.keep
diff --git a/guides/code/getting_started/test/unit/comment_test.rb b/guides/code/getting_started/test/models/comment_test.rb
index b6d6131a96..b6d6131a96 100644
--- a/guides/code/getting_started/test/unit/comment_test.rb
+++ b/guides/code/getting_started/test/models/comment_test.rb
diff --git a/guides/code/getting_started/test/unit/post_test.rb b/guides/code/getting_started/test/models/post_test.rb
index 6d9d463a71..6d9d463a71 100644
--- a/guides/code/getting_started/test/unit/post_test.rb
+++ b/guides/code/getting_started/test/models/post_test.rb
diff --git a/guides/code/getting_started/test/test_helper.rb b/guides/code/getting_started/test/test_helper.rb
index 3daca18a71..f91a4375dc 100644
--- a/guides/code/getting_started/test/test_helper.rb
+++ b/guides/code/getting_started/test/test_helper.rb
@@ -3,6 +3,8 @@ require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
+ ActiveRecord::Migration.check_pending!
+
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
diff --git a/guides/code/getting_started/test/unit/helpers/home_helper_test.rb b/guides/code/getting_started/test/unit/helpers/home_helper_test.rb
deleted file mode 100644
index 4740a18dac..0000000000
--- a/guides/code/getting_started/test/unit/helpers/home_helper_test.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'test_helper'
-
-class HomeHelperTest < ActionView::TestCase
-end
diff --git a/guides/code/getting_started/vendor/assets/javascripts/.keep b/guides/code/getting_started/vendor/assets/javascripts/.keep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/guides/code/getting_started/vendor/assets/javascripts/.keep
diff --git a/guides/code/getting_started/vendor/assets/stylesheets/.keep b/guides/code/getting_started/vendor/assets/stylesheets/.keep
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/guides/code/getting_started/vendor/assets/stylesheets/.keep
diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md
index 52571fab60..9c157ec0b3 100644
--- a/guides/source/4_0_release_notes.md
+++ b/guides/source/4_0_release_notes.md
@@ -3,11 +3,10 @@ Ruby on Rails 4.0 Release Notes
Highlights in Rails 4.0:
-* Ruby 1.9.3 only
+* Ruby 2.0 preferred; 1.9.3+ required
* Strong Parameters
* Turbolinks
* Russian Doll Caching
-* Asynchronous Mailers
These release notes cover only the major changes. To know about various bug fixes and changes, please refer to the change logs or check out the [list of commits](https://github.com/rails/rails/commits/master) in the main Rails repository on GitHub.
@@ -150,7 +149,7 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activ
Action Pack
-----------
-Please refer to the [Changelog](https://github.com/rails/rails/blob/master/railties/CHANGELOG.md) for detailed changes.
+Please refer to the [Changelog](https://github.com/rails/rails/blob/master/actionpack/CHANGELOG.md) for detailed changes.
### Notable changes
@@ -162,7 +161,7 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/railt
Active Record
-------------
-Please refer to the [Changelog](https://github.com/rails/rails/blob/master/railties/CHANGELOG.md) for detailed changes.
+Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activerecord/CHANGELOG.md) for detailed changes.
### Notable changes
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index cc80334af3..7260a48c8c 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -58,7 +58,7 @@ Parameters
You will probably want to access data sent in by the user or other parameters in your controller actions. There are two kinds of parameters possible in a web application. The first are parameters that are sent as part of the URL, called query string parameters. The query string is everything after "?" in the URL. The second type of parameter is usually referred to as POST data. This information usually comes from an HTML form which has been filled in by the user. It's called POST data because it can only be sent as part of an HTTP POST request. Rails does not make any distinction between query string parameters and POST parameters, and both are available in the `params` hash in your controller:
```ruby
-class ClientsController < ActionController::Base
+class ClientsController < ApplicationController
# This action uses query string parameters because it gets run
# by an HTTP GET request, but this does not make any difference
# to the way in which the parameters are accessed. The URL for
@@ -479,7 +479,7 @@ In addition to "before" filters, you can also run filters after an action has be
For example, in a website where changes have an approval workflow an administrator could be able to preview them easily, just apply them within a transaction:
```ruby
-class ChangesController < ActionController::Base
+class ChangesController < ApplicationController
around_action :wrap_in_transaction, only: :show
private
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index 7c6ef52f4a..513ae1272f 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -575,3 +575,25 @@ end
```
In the test we send the email and store the returned object in the `email` variable. We then ensure that it was sent (the first assert), then, in the second batch of assertions, we ensure that the email does indeed contain what we expect.
+
+NOTE: The `ActionMailer::Base.deliveries` array is only reset automatically in `ActionMailer::TestCase` tests. If you want to have a clean slate outside Action Mailer tests, you can reset it manually with: `ActionMailer::Base.deliveries.clear`
+
+Intercepting Emails
+-------------------
+There are situations where you need to edit an email before it's delivered. Fortunately Action Mailer provides hooks to intercept every email. You can register an interceptor to make modifications to mail messages right before they are handed to the delivery agents.
+
+```ruby
+class SandboxEmailInterceptor
+ def self.delivering_email(message)
+ message.to = ['sandbox@example.com']
+ end
+end
+```
+
+Before the interceptor can do its job you need to register it with the Action Mailer framework. You can do this in an initializer file `config/initializers/sandbox_email_interceptor.rb`
+
+```ruby
+ActionMailer::Base.register_interceptor(SandboxEmailInterceptor) if Rails.env.staging?
+```
+
+NOTE: The example above uses a custom environment called "staging" for a production like server but for testing purposes. You can read [Creating Rails environments](./configuring.html#creating-rails-environments) for more information about custom Rails environments.
diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md
index 062bcd49f4..69d7333e6f 100644
--- a/guides/source/active_record_basics.md
+++ b/guides/source/active_record_basics.md
@@ -1,6 +1,6 @@
Active Record Basics
====================
-
+
This guide is an introduction to Active Record.
After reading this guide, you will know:
diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md
index 3747b00b82..516457bcd3 100644
--- a/guides/source/active_record_callbacks.md
+++ b/guides/source/active_record_callbacks.md
@@ -157,7 +157,6 @@ The following methods trigger callbacks:
* `save!`
* `save(validate: false)`
* `toggle!`
-* `update`
* `update_attribute`
* `update`
* `update!`
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 3c1bb0f132..f02b377832 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -1233,6 +1233,8 @@ The method `squish` strips leading and trailing whitespace, and substitutes runs
There's also the destructive version `String#squish!`.
+Note that it handles both ASCII and Unicode whitespace like mongolian vowel separator (U+180E).
+
NOTE: Defined in `active_support/core_ext/string/filters.rb`.
### `truncate`
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index fffa31927d..e939606c88 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -37,9 +37,9 @@ You should use the defaults for all new applications unless you have a specific
### Main Features
-The first feature of the pipeline is to concatenate assets. This is important in a production environment, because it can reduce the number of requests that a browser must make to render a web page. Web browsers are limited in the number of requests that they can make in parallel, so fewer requests can mean faster loading for your application.
+The first feature of the pipeline is to concatenate assets. This is important in a production environment, because it can reduce the number of requests that a browser makes to render a web page. Web browsers are limited in the number of requests that they can make in parallel, so fewer requests can mean faster loading for your application.
-Rails 2.x introduced the ability to concatenate JavaScript and CSS assets by placing `:cache => true` at the end of the `javascript_include_tag` and `stylesheet_link_tag` methods. But this technique has some limitations. For example, it cannot generate the caches in advance, and it is not able to transparently include assets provided by third-party libraries.
+Rails 2.x introduced the ability to concatenate JavaScript and CSS assets by placing `cache: true` at the end of the `javascript_include_tag` and `stylesheet_link_tag` methods. But this technique has some limitations. For example, it cannot generate the caches in advance, and it is not able to transparently include assets provided by third-party libraries.
Starting with version 3.1, Rails defaults to concatenating all JavaScript files into one master `.js` file and all CSS files into one master `.css` file. As you'll learn later in this guide, you can customize this strategy to group files any way you like. In production, Rails inserts an MD5 fingerprint into each filename so that the file is cached by the web browser. You can invalidate the cache by altering this fingerprint, which happens automatically whenever you change the file contents.
@@ -369,8 +369,8 @@ If any of the files in the manifest have changed between requests, the server re
Debug mode can also be enabled in the Rails helper methods:
```erb
-<%= stylesheet_link_tag "application", :debug => true %>
-<%= javascript_include_tag "application", :debug => true %>
+<%= stylesheet_link_tag "application", debug: true %>
+<%= javascript_include_tag "application", debug: true %>
```
The `:debug` option is redundant if debug mode is on.
@@ -445,7 +445,7 @@ NOTE. If you are precompiling your assets locally, you can use `bundle install -
The default matcher for compiling files includes `application.js`, `application.css` and all non-JS/CSS files (this will include all image assets automatically):
```ruby
-[ Proc.new{ |path| !%w(.js .css).include?(File.extname(path)) }, /application.(css|js)$/ ]
+[ Proc.new { |path| !%w(.js .css).include?(File.extname(path)) }, /application.(css|js)$/ ]
```
NOTE. The matcher (and other members of the precompile array; see below) is applied to final compiled file names. This means that anything that compiles to JS/CSS is excluded, as well as raw JS/CSS files; for example, `.coffee` and `.scss` files are **not** automatically included as they compile to JS/CSS.
@@ -460,7 +460,7 @@ Or you can opt to precompile all assets with something like this:
```ruby
# config/environments/production.rb
-config.assets.precompile << Proc.new { |path|
+config.assets.precompile << Proc.new do |path|
if path =~ /\.(css|js)\z/
full_path = Rails.application.assets.resolve(path).to_path
app_assets_path = Rails.root.join('app', 'assets').to_path
@@ -474,7 +474,7 @@ config.assets.precompile << Proc.new { |path|
else
false
end
-}
+end
```
NOTE. Always specify an expected compiled filename that ends with js or css, even if you want to add Sass or CoffeeScript files to the precompile array.
@@ -502,14 +502,14 @@ For Apache:
```apache
# The Expires* directives requires the Apache module `mod_expires` to be enabled.
-<LocationMatch "^/assets/.*$">
+<Location /assets/>
# Use of ETag is discouraged when Last-Modified is present
Header unset ETag
FileETag None
# RFC says only cache for 1 year
ExpiresActive On
ExpiresDefault "access plus 1 year"
-</LocationMatch>
+</Location>
```
For nginx:
@@ -663,7 +663,7 @@ class Transformer
end
```
-To enable this, pass a `new` object to the config option in `application.rb`:
+To enable this, pass a new object to the config option in `application.rb`:
```ruby
config.assets.css_compressor = Transformer.new
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index 0228d463cf..a270ec7a7e 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -85,6 +85,51 @@ This fragment is then available to all actions in the `ProductsController` using
```ruby
expire_fragment('all_available_products')
```
+If you want to avoid expiring the fragment manually, whenever an action updates a product, you can define a helper method:
+
+```ruby
+module ProductsHelper
+ def cache_key_for_products
+ count = Product.count
+ max_updated_at = Product.maximum(:updated_at).try(:utc).try(:to_s, :number)
+ "products/all-#{count}-#{max_updated_at}"
+ end
+end
+```
+
+This method generates a cache key that depends on all products and can be used in the view:
+
+```erb
+<% cache(cache_key_for_products) do %>
+ All available products:
+<% end %>
+```
+You can also use an Active Record model as the cache key:
+
+```erb
+<% Product.all.each do |p| %>
+ <% cache(p) do %>
+ <%= link_to p.name, product_url(p) %>
+ <% end %>
+<% end %>
+```
+
+Behind the scenes, a method called `cache_key` will be invoked on the model and it returns a string like `products/23-20130109142513`. The cache key includes the model name, the id and finally the updated_at timestamp. Thus it will automatically generate a new fragment when the product is updated because the key changes.
+
+You can also combine the two schemes which is called "Russian Doll Caching":
+
+```erb
+<% cache(cache_key_for_products) do %>
+ All available products:
+ <% Product.all.each do |p| %>
+ <% cache(p) do %>
+ <%= link_to p.name, product_url(p) %>
+ <% end %>
+ <% end %>
+<% end %>
+```
+
+It's called "Russian Doll Caching" because it nests multiple fragments. The advantage is that if a single product is updated, all the other inner fragments can be reused when regenerating the outer fragment.
### SQL Caching
@@ -93,7 +138,7 @@ Query caching is a Rails feature that caches the result set returned by each que
For example:
```ruby
-class ProductsController < ActionController
+class ProductsController < ApplicationController
def index
# Run a find query
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 12d39ea1cc..9d1fb03fab 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -355,7 +355,7 @@ rake assets:clean # Remove compiled assets
rake assets:precompile # Compile all the assets named in config.assets.precompile
rake db:create # Create the database from config/database.yml for the current Rails.env
...
-rake log:clear # Truncates all *.log files in log/ to zero bytes
+rake log:clear # Truncates all *.log files in log/ to zero bytes (specify which logs with LOGS=test,development)
rake middleware # Prints out your Rack middleware stack
...
rake tmp:clear # Clear session, cache, and socket files from tmp/ (narrow w/ tmp:sessions:clear, tmp:cache:clear, tmp:sockets:clear)
@@ -378,7 +378,7 @@ Active Record version 4.0.0.beta
Action Pack version 4.0.0.beta
Action Mailer version 4.0.0.beta
Active Support version 4.0.0.beta
-Middleware ActionDispatch::Static, Rack::Lock, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::EncryptedCookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, Rack::Head, Rack::ConditionalGet, Rack::ETag, ActionDispatch::BestStandardsSupport
+Middleware ActionDispatch::Static, Rack::Lock, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::EncryptedCookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, Rack::Head, Rack::ConditionalGet, Rack::ETag
Application root /home/foobar/commandsapp
Environment development
Database adapter sqlite3
@@ -442,7 +442,7 @@ app/model/post.rb:
NOTE. When using specific annotations and custom annotations, the annotation name (FIXME, BUG etc) is not displayed in the output lines.
-By default, `rake notes` will look in the `app`, `config`, `lib`, `script` and `test` directories. If you would like to search other directories, you can provide them as a comma separated list in an environment variable `SOURCE_ANNOTATION_DIRECTORIES`.
+By default, `rake notes` will look in the `app`, `config`, `lib`, `bin` and `test` directories. If you would like to search other directories, you can provide them as a comma separated list in an environment variable `SOURCE_ANNOTATION_DIRECTORIES`.
```bash
$ export SOURCE_ANNOTATION_DIRECTORIES='rspec,vendor'
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index cd0b99b177..be46e15078 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -207,7 +207,6 @@ Every Rails application comes with a standard set of middleware which it uses in
* `ActionDispatch::ParamsParser` parses out parameters from the request into `params`.
* `Rack::MethodOverride` allows the method to be overridden if `params[:_method]` is set. This is the middleware which supports the PATCH, PUT, and DELETE HTTP method types.
* `ActionDispatch::Head` converts HEAD requests to GET requests and serves them as so.
-* `ActionDispatch::BestStandardsSupport` enables "best standards support" so that IE8 renders some elements correctly.
Besides these usual middleware, you can add your own by using the `config.middleware.use` method:
@@ -230,13 +229,13 @@ config.middleware.insert_after ActionDispatch::Head, Magical::Unicorns
Middlewares can also be completely swapped out and replaced with others:
```ruby
-config.middleware.swap ActionDispatch::BestStandardsSupport, Magical::Unicorns
+config.middleware.swap ActionController::Failsafe, Lifo::Failsafe
```
They can also be removed from the stack completely:
```ruby
-config.middleware.delete ActionDispatch::BestStandardsSupport
+config.middleware.delete "Rack::MethodOverride"
```
### Configuring i18n
@@ -303,7 +302,7 @@ The schema dumper adds one additional configuration option:
* `config.action_controller.permit_all_parameters` sets all the parameters for mass assignment to be permitted by default. The default value is `false`.
-* `config.action_controller.raise_on_unpermitted_parameters` enables raising an exception if parameters that are not explicitly permitted are found. The default value is `true` in development and test environments, `false` otherwise.
+* `config.action_controller.action_on_unpermitted_params` enables logging or raising an exception if parameters that are not explicitly permitted are found. Set to `:log` or `:raise` to enable. The default value is `:log` in development and test environments, and `false` in all other environments.
### Configuring Action Dispatch
@@ -345,34 +344,6 @@ The schema dumper adds one additional configuration option:
* `config.action_view.erb_trim_mode` gives the trim mode to be used by ERB. It defaults to `'-'`. See the [ERB documentation](http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/) for more information.
-* `config.action_view.javascript_expansions` is a hash containing expansions that can be used for the JavaScript include tag. By default, this is defined as:
-
- ```ruby
- config.action_view.javascript_expansions = { :defaults => %w(jquery jquery_ujs) }
- ```
-
- However, you may add to this by defining others:
-
- ```ruby
- config.action_view.javascript_expansions[:prototype] = [
- 'prototype', 'effects', 'dragdrop', 'controls'
- ]
- ```
-
- And can reference in the view with the following code:
-
- ```ruby
- <%= javascript_include_tag :prototype %>
- ```
-
-* `config.action_view.stylesheet_expansions` works in much the same way as `javascript_expansions`, but has no default key. Keys defined for this hash can be referenced in the view like such:
-
- ```ruby
- <%= stylesheet_link_tag :special %>
- ```
-
-* `config.action_view.cache_asset_ids` With the cache enabled, the asset tag helper methods will make fewer expensive file system calls (the default implementation checks the file system timestamp). However this prevents you from modifying any asset files while the server is running.
-
* `config.action_view.embed_authenticity_token_in_remote_forms` allows you to set the default behavior for `authenticity_token` in forms with `:remote => true`. By default it's set to false, which means that remote forms will not include `authenticity_token`, which is helpful when you're fragment-caching the form. Remote forms get the authenticity from the `meta` tag, so embedding is unnecessary unless you support browsers without JavaScript. In such case you can either pass `:authenticity_token => true` as a form option or set this config setting to `true`
* `config.action_view.prefix_partial_path_with_controller_namespace` determines whether or not partials are looked up from a subdirectory in templates rendered from namespaced controllers. For example, consider a controller named `Admin::PostsController` which renders this template:
@@ -552,6 +523,15 @@ development:
Change the username and password in the `development` section as appropriate.
+### Creating Rails Environments
+
+By default Rails ships with three environments: "development", "test", and "production". While these are sufficient for most use cases, there are circumstances when you want more environments.
+
+Imagine you have a server which mirrors the production environment but is only used for testing. Such a server is commonly called a "staging server". To define an environment called "staging" for this server just by create a file called `config/environments/staging.rb`. Please use the contents of any existing file in `config/environments` as a starting point and make the necessary changes from there.
+
+That environment is no different than the default ones, start a server with `rails server -e staging`, a console with `rails console staging`, `Rails.env.staging?` works, etc.
+
+
Rails Environment Settings
--------------------------
@@ -672,10 +652,6 @@ Below is a comprehensive list of all the initializers found in Rails in the orde
* `action_dispatch.configure` Configures the `ActionDispatch::Http::URL.tld_length` to be set to the value of `config.action_dispatch.tld_length`.
-* `action_view.cache_asset_ids` Sets `ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids` to `false` when Active Support loads, but only if `config.cache_classes` is too.
-
-* `action_view.javascript_expansions` Registers the expansions set up by `config.action_view.javascript_expansions` and `config.action_view.stylesheet_expansions` to be recognized by Action View and therefore usable in the views.
-
* `action_view.set_configs` Sets up Action View by using the settings in `config.action_view` by `send`'ing the method names as setters to `ActionView::Base` and passing the values through.
* `action_controller.logger` Sets `ActionController::Base.logger` — if it's not already set — to `Rails.logger`.
diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md
index db43d62fcf..6493c1e1ec 100644
--- a/guides/source/development_dependencies_install.md
+++ b/guides/source/development_dependencies_install.md
@@ -174,6 +174,20 @@ $ cd activerecord
$ bundle exec rake postgresql:build_databases
```
+It is possible to build databases for both PostgreSQL and MySQL with
+
+```bash
+$ cd activerecord
+$ bundle exec rake db:create
+```
+
+You can cleanup the databases using
+
+```bash
+$ cd activerecord
+$ bundle exec rake db:drop
+```
+
NOTE: Using the rake task to create the test databases ensures they have the correct character set and collation.
NOTE: You'll see the following warning (or localized warning) during activating HStore extension in PostgreSQL 9.1.x or earlier: "WARNING: => is deprecated as an operator".
diff --git a/guides/source/engines.md b/guides/source/engines.md
index f35233993c..00939c4ff2 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -28,7 +28,7 @@ Engines can also be isolated from their host applications. This means that an ap
It's important to keep in mind at all times that the application should **always** take precedence over its engines. An application is the object that has final say in what goes on in the universe (with the universe being the application's environment) where the engine should only be enhancing it, rather than changing it drastically.
-To see demonstrations of other engines, check out [Devise](https://github.com/plataformatec/devise), an engine that provides authentication for its parent applications, or [Forem](https://github.com/radar/forem), an engine that provides forum functionality. There's also [Spree](https://github.com/spree/spree) which provides an e-commerce platform, and [RefineryCMS](https://github.com/resolve/refinerycms), a CMS engine.
+To see demonstrations of other engines, check out [Devise](https://github.com/plataformatec/devise), an engine that provides authentication for its parent applications, or [Forem](https://github.com/radar/forem), an engine that provides forum functionality. There's also [Spree](https://github.com/spree/spree) which provides an e-commerce platform, and [RefineryCMS](https://github.com/refinery/refinerycms), a CMS engine.
Finally, engines would not have been possible without the work of James Adam, Piotr Sarnacki, the Rails Core Team, and a number of other people. If you ever meet them, don't forget to say thanks!
@@ -57,7 +57,7 @@ The `--full` option tells the generator that you want to create an engine, inclu
end
```
* A file at `lib/blorgh/engine.rb` which is identical in function to a standard Rails application's `config/application.rb` file:
-
+
```ruby
module Blorgh
class Engine < ::Rails::Engine
@@ -72,12 +72,12 @@ The `--mountable` option tells the generator that you want to create a "mountabl
* A namespaced `ApplicationHelper` stub
* A layout view template for the engine
* Namespace isolation to `config/routes.rb`:
-
+
```ruby
Blorgh::Engine.routes.draw do
end
```
-
+
* Namespace isolation to `lib/blorgh/engine.rb`:
```ruby
@@ -261,9 +261,9 @@ end
This helps prevent conflicts with any other engine or application that may have a post resource as well.
-Finally, two files that are the assets for this resource are generated, `app/assets/javascripts/blorgh/posts.js` and `app/assets/javascripts/blorgh/posts.css`. You'll see how to use these a little later.
+Finally, two files that are the assets for this resource are generated, `app/assets/javascripts/blorgh/posts.js` and `app/assets/stylesheets/blorgh/posts.css`. You'll see how to use these a little later.
-By default, the scaffold styling is not applied to the engine as the engine's layout file, `app/views/blorgh/application.html.erb` doesn't load it. To make this apply, insert this line into the `<head>` tag of this layout:
+By default, the scaffold styling is not applied to the engine as the engine's layout file, `app/views/layouts/blorgh/application.html.erb` doesn't load it. To make this apply, insert this line into the `<head>` tag of this layout:
```erb
<%= stylesheet_link_tag "scaffold" %>
@@ -288,7 +288,7 @@ Now people will only need to go to the root of the engine to see all the posts,
### Generating a comments resource
-Now that the engine can to create new blog posts, it only makes sense to add commenting functionality as well. To do get this, you'll need to generate a comment model, a comment controller and then modify the posts scaffold to display comments and allow people to create new ones.
+Now that the engine can create new blog posts, it only makes sense to add commenting functionality as well. To do this, you'll need to generate a comment model, a comment controller and then modify the posts scaffold to display comments and allow people to create new ones.
Run the model generator and tell it to generate a `Comment` model, with the related table having two columns: a `post_id` integer and `text` text column.
@@ -395,7 +395,7 @@ def create
@post = Post.find(params[:post_id])
@comment = @post.comments.create(params[:comment])
flash[:notice] = "Comment has been created!"
- redirect_to post_path
+ redirect_to posts_path
end
```
@@ -470,7 +470,7 @@ If you have multiple engines that need migrations copied over, use `railties:ins
$ rake railties:install:migrations
```
-This command, when run for the first time will copy over all the migrations from the engine. When run the next time, it will only copy over migrations that haven't been copied over already. The first run for this command will output something such as this:
+This command, when run for the first time, will copy over all the migrations from the engine. When run the next time, it will only copy over migrations that haven't been copied over already. The first run for this command will output something such as this:
```bash
Copied migration [timestamp_1]_create_blorgh_posts.rb from blorgh
@@ -650,6 +650,14 @@ self.author = Blorgh.user_class.find_or_create_by(name: author_name)
Resulting in something a little shorter, and more implicit in its behavior. The `user_class` method should always return a `Class` object.
+Since we changed the `user_class` method to no longer return a
+`String` but a `Class` we must also modify our `belongs_to` definition
+in the `Blorgh::Post` model:
+
+```ruby
+belongs_to :author, class_name: Blorgh.user_class.to_s
+```
+
To set this configuration setting within the application, an initializer should be used. By using an initializer, the configuration will be set up before the application starts and calls the engine's models which may depend on this configuration setting existing.
Create a new initializer at `config/initializers/blorgh.rb` inside the application where the `blorgh` engine is installed and put this content in it:
@@ -754,10 +762,9 @@ end
#### Implementing Decorator Pattern Using ActiveSupport::Concern
-Using `Class#class_eval` is great for simple adjustments, but for more complex class modifications, you might want to consider using [`ActiveSupport::Concern`](http://edgeapi.rubyonrails.org/classes/ActiveSupport/Concern.html) helps manage load order of interlinked dependencies at run time allowing you to significantly modularize your code.
+Using `Class#class_eval` is great for simple adjustments, but for more complex class modifications, you might want to consider using [`ActiveSupport::Concern`](http://edgeapi.rubyonrails.org/classes/ActiveSupport/Concern.html). ActiveSupport::Concern manages load order of interlinked dependent modules and classes at run time allowing you to significantly modularize your code.
-**Adding** `Post#time_since_created`<br/>
-**Overriding** `Post#summary`
+**Adding** `Post#time_since_created` and **Overriding** `Post#summary`
```ruby
# MyApp/app/models/blorgh/post.rb
@@ -790,7 +797,7 @@ module Blorgh::Concerns::Models::Post
extend ActiveSupport::Concern
# 'included do' causes the included code to be evaluated in the
- # conext where it is included (post.rb), rather than be
+ # context where it is included (post.rb), rather than be
# executed in the module's context (blorgh/concerns/models/post).
included do
attr_accessor :author_name
@@ -800,9 +807,9 @@ module Blorgh::Concerns::Models::Post
private
- def set_author
- self.author = User.find_or_create_by(name: author_name)
- end
+ def set_author
+ self.author = User.find_or_create_by(name: author_name)
+ end
end
def summary
@@ -840,7 +847,7 @@ Try this now by creating a new file at `app/views/blorgh/posts/index.html.erb` a
### Routes
-Routes inside an engine are, by default, isolated from the application. This is done by the `isolate_namespace` call inside the `Engine` class. This essentially means that the application and its engines can have identically named routes, and that they will not clash.
+Routes inside an engine are, by default, isolated from the application. This is done by the `isolate_namespace` call inside the `Engine` class. This essentially means that the application and its engines can have identically named routes and they will not clash.
Routes inside an engine are drawn on the `Engine` class within `config/routes.rb`, like this:
diff --git a/guides/source/generators.md b/guides/source/generators.md
index 8b91dfc5a5..1a08eb420a 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -310,7 +310,7 @@ In Rails 3.0 and above, generators don't just look in the source root for templa
```erb
module <%= class_name %>Helper
- attr_reader :<%= plural_name %>, <%= plural_name.singularize %>
+ attr_reader :<%= plural_name %>, :<%= plural_name.singularize %>
end
```
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index 7d86b3866a..87f5e43157 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -21,7 +21,7 @@ application from scratch. It does not assume that you have any prior experience
with Rails. However, to get the most out of it, you need to have some
prerequisites installed:
-* The [Ruby](http://www.ruby-lang.org/en/downloads) language version 1.9.3 or higher
+* The [Ruby](http://www.ruby-lang.org/en/downloads) language version 1.9.3 or newer
* The [RubyGems](http://rubygems.org/) packaging system
* To learn more about RubyGems, please read the [RubyGems User Guide](http://docs.rubygems.org/read/book/1)
* A working installation of the [SQLite3 Database](http://www.sqlite.org)
@@ -84,7 +84,7 @@ current version of Ruby installed:
```bash
$ ruby -v
-ruby 1.9.3p327
+ruby 1.9.3p385
```
To install Rails, use the `gem install` command provided by RubyGems:
@@ -583,9 +583,31 @@ content:
</p>
```
-Finally, if you now go to
-<http://localhost:3000/posts/new> you'll
-be able to create a post. Try it!
+If you now go to
+<http://localhost:3000/posts/new> you'll *almost* be able to create a post. Try
+it! You should get an error that looks like this:
+
+![Forbidden attributes for new post](images/getting_started/forbidden_attributes_for_new_post.png)
+
+Rails has several security features that help you write secure applications,
+and you're running into one of them now. This one is called
+'strong_parameters,' which requires us to tell Rails exactly which parameters
+we want to accept in our controllers. In this case, we want to allow the
+'title' and 'text' parameters, so change your `create` controller action to
+look like this:
+
+```
+ def create
+ @post = Post.new(params[:post].permit(:title, :text))
+
+ @post.save
+ redirect_to action: :show, id: @post.id
+ end
+```
+
+See the `permit`? It allows us to accept both `title` and `text` in this
+action. With this change, you should finally be able to create new `Post`s.
+Visit <http://localhost:3000/posts/new> and give it a try!
![Show action for posts](images/getting_started/show_action_for_posts.png)
@@ -711,10 +733,11 @@ class Post < ActiveRecord::Base
end
```
-These changes will ensure that all posts have a title that is at least five characters long.
-Rails can validate a variety of conditions in a model, including the presence or uniqueness of columns, their
-format, and the existence of associated objects. Validations are covered in detail
-in [Active Record Validations and Callbacks](active_record_validations_callbacks.html#validations-overview)
+These changes will ensure that all posts have a title that is at least five
+characters long. Rails can validate a variety of conditions in a model,
+including the presence or uniqueness of columns, their format, and the
+existence of associated objects. Validations are covered in detail in [Active
+Record Validations](active_record_validations.html)
With the validation now in place, when you call `@post.save` on an invalid
post, it will return `false`. If you open `app/controllers/posts_controller.rb`
@@ -729,7 +752,7 @@ def new
end
def create
- @post = Post.new(params[:post])
+ @post = Post.new(params[:post].permit(:title, :text))
if @post.save
redirect_to action: :show, id: @post.id
@@ -864,8 +887,8 @@ method: :patch do |f| %>
This time we point the form to the `update` action, which is not defined yet
but will be very soon.
-The `method: :patch` option tells Rails that we want this form to be
-submitted via the `PUT` HTTP method which is the HTTP method you're expected to use to
+The `method: :patch` option tells Rails that we want this form to be submitted
+via the `PATCH` HTTP method which is the HTTP method you're expected to use to
**update** resources according to the REST protocol.
TIP: By default forms built with the _form_for_ helper are sent via `POST`.
@@ -883,7 +906,7 @@ And then create the `update` action in `app/controllers/posts_controller.rb`:
def update
@post = Post.find(params[:id])
- if @post.update(params[:post])
+ if @post.update(params[:post].permit(:title, :text))
redirect_to action: :show, id: @post.id
else
render 'edit'
@@ -1388,7 +1411,7 @@ Let's wire up the `create` in `app/controllers/comments_controller.rb`:
class CommentsController < ApplicationController
def create
@post = Post.find(params[:post_id])
- @comment = @post.comments.create(params[:comment])
+ @comment = @post.comments.create(params[:comment].permit(:commenter, :body))
redirect_to post_path(@post)
end
end
@@ -1559,6 +1582,9 @@ Then you make the `app/views/posts/show.html.erb` look like the following:
<%= @post.text %>
</p>
+<h2>Comments</h2>
+<%= render @post.comments %>
+
<h2>Add a comment:</h2>
<%= render "comments/form" %>
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index 69232d9bd4..5304ca4285 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -102,7 +102,7 @@ The **translations load path** (`I18n.load_path`) is just a Ruby Array of paths
NOTE: 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.
-The default initializer `locale.rb` file has instructions on how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines.
+The default `application.rb` files has instructions on how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines.
```ruby
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index 7c26512046..339008ab9e 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -695,72 +695,6 @@ To include `http://example.com/main.js`:
<%= javascript_include_tag "http://example.com/main.js" %>
```
-If the application does not use the asset pipeline, the `:defaults` option loads jQuery by default:
-
-```erb
-<%= javascript_include_tag :defaults %>
-```
-
-Outputting `script` tags such as this:
-
-```html
-<script src="/javascripts/jquery.js"></script>
-<script src="/javascripts/jquery_ujs.js"></script>
-```
-
-These two files for jQuery, `jquery.js` and `jquery_ujs.js` must be placed inside `public/javascripts` if the application doesn't use the asset pipeline. These files can be downloaded from the [jquery-rails repository on GitHub](https://github.com/indirect/jquery-rails/tree/master/vendor/assets/javascripts)
-
-WARNING: If you are using the asset pipeline, this tag will render a `script` tag for an asset called `defaults.js`, which would not exist in your application unless you've explicitly created it.
-
-And you can in any case override the `:defaults` expansion in `config/application.rb`:
-
-```ruby
-config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js)
-```
-
-You can also define new defaults:
-
-```ruby
-config.action_view.javascript_expansions[:projects] = %w(projects.js tickets.js)
-```
-
-And use them by referencing them exactly like `:defaults`:
-
-```erb
-<%= javascript_include_tag :projects %>
-```
-
-When using `:defaults`, if an `application.js` file exists in `public/javascripts` it will be included as well at the end.
-
-Also, if the asset pipeline is disabled, the `:all` expansion loads every JavaScript file in `public/javascripts`:
-
-```erb
-<%= javascript_include_tag :all %>
-```
-
-Note that your defaults of choice will be included first, so they will be available to all subsequently included files.
-
-You can supply the `:recursive` option to load files in subfolders of `public/javascripts` as well:
-
-```erb
-<%= javascript_include_tag :all, recursive: true %>
-```
-
-If you're loading multiple JavaScript files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify `cache: true` in your `javascript_include_tag`:
-
-```erb
-<%= javascript_include_tag "main", "columns", cache: true %>
-```
-
-By default, the combined file will be delivered as `javascripts/all.js`. You can specify a location for the cached asset file instead:
-
-```erb
-<%= javascript_include_tag "main", "columns",
- cache: "cache/main/display" %>
-```
-
-You can even use dynamic paths such as `cache/#{current_site}/main/display`.
-
#### Linking to CSS Files with the `stylesheet_link_tag`
The `stylesheet_link_tag` helper returns an HTML `<link>` tag for each source provided.
@@ -797,33 +731,6 @@ By default, the `stylesheet_link_tag` creates links with `media="screen" rel="st
<%= stylesheet_link_tag "main_print", media: "print" %>
```
-If the asset pipeline is disabled, the `all` option links every CSS file in `public/stylesheets`:
-
-```erb
-<%= stylesheet_link_tag :all %>
-```
-
-You can supply the `:recursive` option to link files in subfolders of `public/stylesheets` as well:
-
-```erb
-<%= stylesheet_link_tag :all, recursive: true %>
-```
-
-If you're loading multiple CSS files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify `cache: true` in your `stylesheet_link_tag`:
-
-```erb
-<%= stylesheet_link_tag "main", "columns", cache: true %>
-```
-
-By default, the combined file will be delivered as `stylesheets/all.css`. You can specify a location for the cached asset file instead:
-
-```erb
-<%= stylesheet_link_tag "main", "columns",
- cache: "cache/main/display" %>
-```
-
-You can even use dynamic paths such as `cache/#{current_site}/main/display`.
-
#### Linking to Images with the `image_tag`
The `image_tag` helper builds an HTML `<img />` tag to the specified file. By default, files are loaded from `public/images`.
diff --git a/guides/source/migrations.md b/guides/source/migrations.md
index 617e01bd15..cefbc3b829 100644
--- a/guides/source/migrations.md
+++ b/guides/source/migrations.md
@@ -474,7 +474,7 @@ class ExampleMigration < ActiveRecord::Migration
t.references :category
end
- #add a foreign key
+ # add a foreign key
execute <<-SQL
ALTER TABLE products
ADD CONSTRAINT fk_products_categories
@@ -1011,7 +1011,7 @@ with foreign key constraints in the database.
Although Active Record does not provide any tools for working directly with
such features, the `execute` method can be used to execute arbitrary SQL. You
-could also use some plugin like
+could also use some gem like
[foreigner](https://github.com/matthuhiggins/foreigner) which add foreign key
support to Active Record (including support for dumping foreign keys in
`db/schema.rb`).
diff --git a/guides/source/nested_model_forms.md b/guides/source/nested_model_forms.md
index 93d8e8dfcd..b90b3bb5fc 100644
--- a/guides/source/nested_model_forms.md
+++ b/guides/source/nested_model_forms.md
@@ -98,7 +98,7 @@ A nested model form will _only_ be built if the associated object(s) exist. This
Consider the following typical RESTful controller which will prepare a new Person instance and its `address` and `projects` associations before rendering the `new` template:
```ruby
-class PeopleController < ActionController:Base
+class PeopleController < ApplicationController
def new
@person = Person.new
@person.built_address
diff --git a/guides/source/plugins.md b/guides/source/plugins.md
index f8f04c3c67..695f25f8a9 100644
--- a/guides/source/plugins.md
+++ b/guides/source/plugins.md
@@ -86,7 +86,7 @@ Run `rake` to run the test. This test should fail because we haven't implemented
Great - now you are ready to start development.
-Then in `lib/yaffle.rb` require `lib/core_ext`:
+Then in `lib/yaffle.rb` add `require "yaffle/core_ext"`:
```ruby
# yaffle/lib/yaffle.rb
diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md
index a6119eb433..d8477d89e3 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -137,7 +137,6 @@ use ActionDispatch::ParamsParser
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
-use ActionDispatch::BestStandardsSupport
run MyApp::Application.routes
```
@@ -215,7 +214,6 @@ And to remove browser related middleware,
```ruby
# config/application.rb
-config.middleware.delete "ActionDispatch::BestStandardsSupport"
config.middleware.delete "Rack::MethodOverride"
```
@@ -307,10 +305,6 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol
* Adds ETag header on all String bodies. ETags are used to validate cache.
- **`ActionDispatch::BestStandardsSupport`**
-
-* Enables “best standards support” so that IE8 renders some elements correctly.
-
TIP: It's possible to use any of the above middlewares in your custom Rack stack.
### Using Rack Builder
diff --git a/guides/source/routing.md b/guides/source/routing.md
index 14f23d4020..4614169653 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -832,6 +832,19 @@ will recognize incoming paths beginning with `/photos` but route to the `Images`
NOTE: Use `photos_path`, `new_photo_path`, etc. to generate paths for this resource.
+For namespaced controllers you can use the directory notation. For example:
+
+```ruby
+resources :user_permissions, controller: 'admin/user_permissions'
+```
+
+This will route to the `Admin::UserPermissions` controller.
+
+NOTE: Only the directory notation is supported. specifying the
+controller with ruby constant notation (eg. `:controller =>
+'Admin::UserPermissions'`) can lead to routing problems and results in
+a warning.
+
### Specifying Constraints
You can use the `:constraints` option to specify a required format on the implicit `id`. For example:
diff --git a/guides/source/security.md b/guides/source/security.md
index 3706a61431..769bd130be 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -9,7 +9,6 @@ After reading this guide, you will know:
* The concept of sessions in Rails, what to put in there and popular attack methods.
* How just visiting a site can be a security problem (with CSRF).
* What you have to pay attention to when working with files or providing an administration interface.
-* The Rails-specific mass assignment problem.
* How to manage users: Logging in and out and attack methods on all layers.
* And the most popular injection attack methods.
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 09d6d2d5ee..39a44794a7 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -943,7 +943,7 @@ Cheers!
This is the right time to understand a little more about writing tests for your mailers. The line `ActionMailer::Base.delivery_method = :test` in `config/environments/test.rb` 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 (`ActionMailer::Base.deliveries`).
-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.
+This way, emails are not actually sent, simply constructed. The precise content of the email can then be checked against what is expected, as in the example above.
### Functional Testing
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index b4a59fe3da..568767d9de 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -16,11 +16,11 @@ The best way to be sure that your application still works after upgrading is to
Rails generally stays close to the latest released Ruby version when it's released:
-* Rails 3 and above requires Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible.
-* Rails 3.2.x will be the last branch to support Ruby 1.8.7.
-* Rails 4 will support only Ruby 1.9.3.
+* Rails 3 and above require Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially. You should upgrade as early as possible.
+* Rails 3.2.x is the last branch to support Ruby 1.8.7.
+* Rails 4 prefers Ruby 2.0 and requires 1.9.3 or newer.
-TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump on to 1.9.2 or 1.9.3 for smooth sailing.
+TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump straight to 1.9.3 for smooth sailing.
Upgrading from Rails 3.2 to Rails 4.0
-------------------------------------
@@ -82,6 +82,8 @@ becomes
get 'こんにちは', controller: 'welcome', action: 'index'
```
+* Rails 4.0 has removed ActionDispatch::BestStandardsSupport middleware, !DOCTYPE html already triggers standards mode per http://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx and ChromeFrame header has been moved to `config.action_dispatch.default_headers`
+
### Active Support
Rails 4.0 removes the `j` alias for `ERB::Util#json_escape` since `j` is already used for `ActionView::Helpers::JavaScriptHelper#escape_javascript`.
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index a7ca531123..03ef770352 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -51,7 +51,7 @@ with an id of `results`.
Rails provides quite a bit of built-in support for building web pages with this
technique. You rarely have to write this code yourself. The rest of this guide
-will show you how Rails can help you write web sites in this manner, but it's
+will show you how Rails can help you write websites in this way, but it's
all built on top of this fairly simple technique.
Unobtrusive JavaScript
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 4f18f4cdd2..997858b3c5 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,5 +1,49 @@
## Rails 4.0.0 (unreleased) ##
+* Add notice message for destroy action in scaffold generator.
+
+ *Rahul P. Chaudhari*
+
+* Add two new test rake tasks to speed up full test runs.
+
+ * `test:all`: run tests quickly by merging all types and not resetting db.
+ * `test:all:db`: run tests quickly, but also reset db.
+
+ *Ryan Davis*
+
+* Add `--rc` option to support the load of a custom rc file during the generation of a new app.
+
+ *Amparo Luna*
+
+* Add `--no-rc` option to skip the loading of railsrc file during the generation of a new app.
+
+ *Amparo Luna*
+
+* Fixes database.yml when creating a new rails application with '.'
+ Fix #8304
+
+ *Jeremy W. Rowe*
+
+* Deprecate the `eager_load_paths` configuration and alias it to `autoload_paths`.
+ Since the default in Rails 4.0 is to run in 'threadsafe' mode we need to eager
+ load all of the paths in `autoload_paths`. This may have unintended consequences
+ if you have added 'lib' to `autoload_paths` such as loading unneeded code or
+ code intended only for development and/or test environments. If this applies to
+ your application you should thoroughly check what is being eager loaded.
+
+ *Andrew White*
+
+* Restore Rails::Engine::Railties#engines with deprecation to ensure
+ compatibility with gems such as Thinking Sphinx
+ Fix #8551
+
+ *Tim Raymond*
+
+* Specify which logs to clear when using the `rake log:clear` task.
+ (e.g. rake log:clear LOGS=test,staging)
+
+ *Matt Bridges*
+
* Allow a `:dirs` key in the `SourceAnnotationExtractor.enumerate` options
to explicitly set the directories to be traversed so it's easier to define
custom rake tasks.
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index cff75872b2..5af7de720c 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -55,6 +55,7 @@ module Rails
autoload :Bootstrap, 'rails/application/bootstrap'
autoload :Configuration, 'rails/application/configuration'
autoload :Finisher, 'rails/application/finisher'
+ autoload :Railties, 'rails/engine/railties'
autoload :RoutesReloader, 'rails/application/routes_reloader'
class << self
@@ -232,11 +233,6 @@ module Rails
config.helpers_paths
end
- def railties #:nodoc:
- @railties ||= Rails::Railtie.subclasses.map(&:instance) +
- Rails::Engine.subclasses.map(&:instance)
- end
-
protected
alias :build_middleware_stack :app
@@ -369,10 +365,6 @@ module Rails
middleware.use ::Rack::Head
middleware.use ::Rack::ConditionalGet
middleware.use ::Rack::ETag, "no-cache"
-
- if config.action_dispatch.best_standards_support
- middleware.use ::ActionDispatch::BestStandardsSupport, config.action_dispatch.best_standards_support
- end
end
end
diff --git a/railties/lib/rails/commands/application.rb b/railties/lib/rails/commands/application.rb
index 2d9708e5b5..2ff29418c6 100644
--- a/railties/lib/rails/commands/application.rb
+++ b/railties/lib/rails/commands/application.rb
@@ -9,12 +9,19 @@ if ARGV.first != "new"
ARGV[0] = "--help"
else
ARGV.shift
- railsrc = File.join(File.expand_path("~"), ".railsrc")
- if File.exist?(railsrc)
- extra_args_string = File.open(railsrc).read
- extra_args = extra_args_string.split(/\n+/).map {|l| l.split}.flatten
- puts "Using #{extra_args.join(" ")} from #{railsrc}"
- ARGV.insert(1, *extra_args)
+ unless ARGV.delete("--no-rc")
+ customrc = ARGV.index{ |x| x.include?("--rc=") }
+ railsrc = if customrc
+ File.expand_path(ARGV.delete_at(customrc).gsub(/--rc=/, ""))
+ else
+ File.join(File.expand_path("~"), '.railsrc')
+ end
+ if File.exist?(railsrc)
+ extra_args_string = File.read(railsrc)
+ extra_args = extra_args_string.split(/\n+/).map {|l| l.split}.flatten
+ puts "Using #{extra_args.join(" ")} from #{railsrc}"
+ ARGV.insert(1, *extra_args)
+ end
end
end
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index 5fa7f043c6..15d13d5f28 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -27,11 +27,11 @@ module Rails
#
# Middlewares can also be completely swapped out and replaced with others:
#
- # config.middleware.swap ActionDispatch::BestStandardsSupport, Magical::Unicorns
+ # config.middleware.swap ActionDispatch::Flash, Magical::Unicorns
#
# And finally they can also be removed from the stack completely:
#
- # config.middleware.delete ActionDispatch::BestStandardsSupport
+ # config.middleware.delete ActionDispatch::Flash
#
class MiddlewareStackProxy
def initialize
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 0ae2f16aba..8a6d1f34aa 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -34,9 +34,8 @@ module Rails
# == Configuration
#
# Besides the +Railtie+ configuration which is shared across the application, in a
- # <tt>Rails::Engine</tt> you can access <tt>autoload_paths</tt>, <tt>eager_load_paths</tt>
- # and <tt>autoload_once_paths</tt>, which, differently from a <tt>Railtie</tt>, are scoped to
- # the current engine.
+ # <tt>Rails::Engine</tt> you can access <tt>autoload_paths</tt> and <tt>autoload_once_paths</tt>,
+ # which, differently from a <tt>Railtie</tt>, are scoped to the current engine.
#
# class MyEngine < Rails::Engine
# # Add a load path for this specific Engine
@@ -456,9 +455,9 @@ module Rails
end
# Eager load the application by loading all ruby
- # files inside eager_load paths.
+ # files inside autoload_paths.
def eager_load!
- config.eager_load_paths.each do |load_path|
+ config.autoload_paths.each do |load_path|
matcher = /\A#{Regexp.escape(load_path)}\/(.*)\.rb\Z/
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
require_dependency file.sub(matcher, '\1')
@@ -466,6 +465,10 @@ module Rails
end
end
+ def railties
+ @railties ||= self.class::Railties.new
+ end
+
# Returns a module with all the helpers defined for the engine.
def helpers
@helpers ||= begin
@@ -554,7 +557,6 @@ module Rails
# Freeze so future modifications will fail rather than do nothing mysteriously
config.autoload_paths.freeze
- config.eager_load_paths.freeze
config.autoload_once_paths.freeze
end
@@ -667,7 +669,7 @@ module Rails
end
def _all_autoload_paths #:nodoc:
- @_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq
+ @_all_autoload_paths ||= (config.autoload_paths + config.autoload_once_paths).uniq
end
def _all_load_paths #:nodoc:
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 10d1821709..2b23d8be38 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -4,7 +4,7 @@ module Rails
class Engine
class Configuration < ::Rails::Railtie::Configuration
attr_reader :root
- attr_writer :middleware, :eager_load_paths, :autoload_once_paths, :autoload_paths
+ attr_writer :middleware, :autoload_once_paths, :autoload_paths
def initialize(root=nil)
super()
@@ -39,16 +39,16 @@ module Rails
@paths ||= begin
paths = Rails::Paths::Root.new(@root)
- paths.add "app", eager_load: true, glob: "*"
+ paths.add "app", autoload: true, glob: "*"
paths.add "app/assets", glob: "*"
- paths.add "app/controllers", eager_load: true
- paths.add "app/helpers", eager_load: true
- paths.add "app/models", eager_load: true
- paths.add "app/mailers", eager_load: true
+ paths.add "app/controllers", autoload: true
+ paths.add "app/helpers", autoload: true
+ paths.add "app/models", autoload: true
+ paths.add "app/mailers", autoload: true
paths.add "app/views"
- paths.add "app/controllers/concerns", eager_load: true
- paths.add "app/models/concerns", eager_load: true
+ paths.add "app/controllers/concerns", autoload: true
+ paths.add "app/models/concerns", autoload: true
paths.add "lib", load_path: true
paths.add "lib/assets", glob: "*"
@@ -76,7 +76,13 @@ module Rails
end
def eager_load_paths
- @eager_load_paths ||= paths.eager_load
+ ActiveSupport::Deprecation.warn "eager_load_paths is deprecated and all autoload_paths are now eagerly loaded."
+ autoload_paths
+ end
+
+ def eager_load_paths=(paths)
+ ActiveSupport::Deprecation.warn "eager_load_paths is deprecated and all autoload_paths are now eagerly loaded."
+ self.autoload_paths = paths
end
def autoload_once_paths
diff --git a/railties/lib/rails/engine/railties.rb b/railties/lib/rails/engine/railties.rb
new file mode 100644
index 0000000000..1081700cd0
--- /dev/null
+++ b/railties/lib/rails/engine/railties.rb
@@ -0,0 +1,29 @@
+module Rails
+ class Engine < Railtie
+ class Railties
+ include Enumerable
+ attr_reader :_all
+
+ def initialize
+ @_all ||= ::Rails::Railtie.subclasses.map(&:instance) +
+ ::Rails::Engine.subclasses.map(&:instance)
+ end
+
+ def self.engines
+ @engines ||= ::Rails::Engine.subclasses.map(&:instance)
+ end
+
+ def each(*args, &block)
+ _all.each(*args, &block)
+ end
+
+ def -(others)
+ _all - others
+ end
+
+ delegate :engines, to: "self.class"
+ end
+ end
+end
+
+ActiveSupport::Deprecation.deprecate_methods(Rails::Engine::Railties, :engines)
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index ca3652c703..99d80b3245 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -20,10 +20,10 @@ module Rails
def self.add_shared_options_for(name)
class_option :builder, type: :string, aliases: '-b',
- desc: "Path to a #{name} builder (can be a filesystem path or URL)"
+ desc: "Path to some #{name} builder (can be a filesystem path or URL)"
class_option :template, type: :string, aliases: '-m',
- desc: "Path to an #{name} template (can be a filesystem path or URL)"
+ desc: "Path to some #{name} template (can be a filesystem path or URL)"
class_option :skip_gemfile, type: :boolean, default: false,
desc: "Don't create a Gemfile"
@@ -61,6 +61,12 @@ module Rails
class_option :skip_test_unit, type: :boolean, aliases: '-T', default: false,
desc: 'Skip Test::Unit files'
+ class_option :rc, type: :string, default: false,
+ desc: "Path to file containing extra configuration options for rails command"
+
+ class_option :no_rc, type: :boolean, default: false,
+ desc: 'Skip loading of extra configuration options from .railsrc file'
+
class_option :help, type: :boolean, aliases: '-h', group: :rails,
desc: 'Show this help message and quit'
end
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index e22be40381..b2d1be9b51 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -236,7 +236,7 @@ module Rails
end
def app_name
- @app_name ||= defined_app_const_base? ? defined_app_name : File.basename(destination_root)
+ @app_name ||= (defined_app_const_base? ? defined_app_name : File.basename(destination_root)).tr(".", "_")
end
def defined_app_name
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/bundle b/railties/lib/rails/generators/rails/app/templates/bin/bundle
index e0df7f4440..1123dcf501 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/bundle
+++ b/railties/lib/rails/generators/rails/app/templates/bin/bundle
@@ -1,3 +1,2 @@
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
-require 'rubygems'
load Gem.bin_path('bundler', 'bundle')
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index f5d7d698a3..d149413e2e 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -22,6 +22,14 @@ module <%= app_const_base %>
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
+
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
+ # config.time_zone = 'Central Time (US & Canada)'
+
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
+ # config.i18n.default_locale = :de
<% if options.skip_sprockets? -%>
# Disable the asset pipeline.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index bd0a0d44b8..c4cc1162a4 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -19,9 +19,6 @@
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
- # Only use best-standards-support built into browsers.
- config.action_dispatch.best_standards_support = :builtin
-
<%- unless options.skip_active_record? -%>
# Log the query plan for queries taking more than this (works
# with SQLite, MySQL, and PostgreSQL).
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
index e813437d75..72281a2fef 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
@@ -47,7 +47,7 @@ class <%= controller_class_name %>Controller < ApplicationController
# DELETE <%= route_url %>/1
def destroy
@<%= orm_instance.destroy %>
- redirect_to <%= index_helper %>_url
+ redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %>
end
private
diff --git a/railties/lib/rails/info.rb b/railties/lib/rails/info.rb
index aacc1be2fc..592e74726e 100644
--- a/railties/lib/rails/info.rb
+++ b/railties/lib/rails/info.rb
@@ -46,7 +46,7 @@ module Rails
alias inspect to_s
def to_html
- (table = '<table>').tap do
+ '<table>'.tap do |table|
properties.each do |(name, value)|
table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>)
formatted_value = if value.kind_of?(Array)
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index de6795eda2..80ba144441 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -5,13 +5,13 @@ module Rails
# paths by a Hash like API. It requires you to give a physical path on initialization.
#
# root = Root.new "/rails"
- # root.add "app/controllers", eager_load: true
+ # root.add "app/controllers", autoload: true
#
# The command above creates a new root object and add "app/controllers" as a path.
# This means we can get a <tt>Rails::Paths::Path</tt> object back like below:
#
# path = root["app/controllers"]
- # path.eager_load? # => true
+ # path.autoload? # => true
# path.is_a?(Rails::Paths::Path) # => true
#
# The +Path+ object is simply an enumerable and allows you to easily add extra paths:
@@ -30,7 +30,7 @@ module Rails
# root["config/routes"].inspect # => ["config/routes.rb"]
#
# The +add+ method accepts the following options as arguments:
- # eager_load, autoload, autoload_once and glob.
+ # autoload, autoload_once and glob.
#
# Finally, the +Path+ object also provides a few helpers:
#
@@ -85,7 +85,8 @@ module Rails
end
def eager_load
- filter_by(:eager_load?)
+ ActiveSupport::Deprecation.warn "eager_load is deprecated and all autoload_paths are now eagerly loaded."
+ filter_by(:autoload?)
end
def autoload_paths
@@ -124,9 +125,13 @@ module Rails
@glob = options[:glob]
options[:autoload_once] ? autoload_once! : skip_autoload_once!
- options[:eager_load] ? eager_load! : skip_eager_load!
options[:autoload] ? autoload! : skip_autoload!
options[:load_path] ? load_path! : skip_load_path!
+
+ if !options.key?(:autoload) && options.key?(:eager_load)
+ ActiveSupport::Deprecation.warn "the :eager_load option is deprecated and all :autoload paths are now eagerly loaded."
+ options[:eager_load] ? autoload! : skip_autoload!
+ end
end
def children
@@ -143,22 +148,37 @@ module Rails
expanded.last
end
- %w(autoload_once eager_load autoload load_path).each do |m|
+ %w(autoload_once autoload load_path).each do |m|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def #{m}! # def eager_load!
- @#{m} = true # @eager_load = true
+ def #{m}! # def autoload!
+ @#{m} = true # @autoload = true
end # end
#
- def skip_#{m}! # def skip_eager_load!
- @#{m} = false # @eager_load = false
+ def skip_#{m}! # def skip_autoload!
+ @#{m} = false # @autoload = false
end # end
#
- def #{m}? # def eager_load?
- @#{m} # @eager_load
+ def #{m}? # def autoload?
+ @#{m} # @autoload
end # end
RUBY
end
+ def eager_load!
+ ActiveSupport::Deprecation.warn "eager_load paths are deprecated and all autoload paths are now eagerly loaded."
+ autoload!
+ end
+
+ def skip_eager_load!
+ ActiveSupport::Deprecation.warn "eager_load paths are deprecated and all autoload paths are now eagerly loaded."
+ skip_autoload!
+ end
+
+ def eager_load?
+ ActiveSupport::Deprecation.warn "eager_load paths are deprecated and all autoload paths are now eagerly loaded."
+ autoload?
+ end
+
def each(&block)
@paths.each(&block)
end
diff --git a/railties/lib/rails/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb
index 4536fedaa3..3b7f358a5b 100644
--- a/railties/lib/rails/ruby_version_check.rb
+++ b/railties/lib/rails/ruby_version_check.rb
@@ -2,12 +2,12 @@ if RUBY_VERSION < '1.9.3'
desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
abort <<-end_message
- Rails 4 requires Ruby 1.9.3+.
+ Rails 4 prefers to run on Ruby 2.0.
You're running
#{desc}
- Please upgrade to continue.
+ Please upgrade to Ruby 1.9.3 or newer to continue.
end_message
end
diff --git a/railties/lib/rails/tasks/log.rake b/railties/lib/rails/tasks/log.rake
index 6e1334692e..6c3f02eb0c 100644
--- a/railties/lib/rails/tasks/log.rake
+++ b/railties/lib/rails/tasks/log.rake
@@ -1,9 +1,23 @@
namespace :log do
- desc "Truncates all *.log files in log/ to zero bytes"
+ desc "Truncates all *.log files in log/ to zero bytes (specify which logs with LOGS=test,development)"
task :clear do
- FileList["log/*.log"].each do |log_file|
- f = File.open(log_file, "w")
- f.close
+ log_files.each do |file|
+ clear_log_file(file)
end
end
+
+ def log_files
+ if ENV['LOGS']
+ ENV['LOGS'].split(',')
+ .map { |file| "log/#{file.strip}.log" }
+ .select { |file| File.exists?(file) }
+ else
+ FileList["log/*.log"]
+ end
+ end
+
+ def clear_log_file(file)
+ f = File.open(file, "w")
+ f.close
+ end
end
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index f0d46fd959..44485d9b14 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -71,6 +71,18 @@ namespace :test do
end
end
+ # Inspired by: http://ngauthier.com/2012/02/quick-tests-with-bash.html
+ desc "Run tests quickly by merging all types and not resetting db"
+ Rake::TestTask.new(:all) do |t|
+ t.libs << "test"
+ t.pattern = "test/**/*_test.rb"
+ end
+
+ namespace :all do
+ desc "Run tests quickly, but also reset db"
+ task :db => %w[db:test:prepare test:all]
+ end
+
Rake::TestTask.new(recent: "test:prepare") do |t|
since = TEST_CHANGES_SINCE
touched = FileList['test/**/*_test.rb'].select { |path| File.mtime(path) > since } +
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index e39430560f..a55bf012da 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -27,6 +27,6 @@ Gem::Specification.new do |s|
s.add_dependency 'actionpack', version
s.add_dependency 'rake', '>= 0.8.7'
- s.add_dependency 'thor', '>= 0.15.4', '< 2.0'
+ s.add_dependency 'thor', '>= 0.17.0', '< 2.0'
s.add_dependency 'rdoc', '~> 3.4'
end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index f9108ed7b9..7b45623f6c 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -417,17 +417,7 @@ module ApplicationTests
require "#{app_path}/config/environment"
- assert_equal Time.find_zone!("Wellington"), Time.zone_default
- end
-
- test "timezone can be set on initializers" do
- app_file "config/initializers/locale.rb", <<-RUBY
- Rails.application.config.time_zone = "Central Time (US & Canada)"
- RUBY
-
- require "#{app_path}/config/environment"
-
- assert_equal Time.find_zone!("Central Time (US & Canada)"), Time.zone_default
+ assert_equal "Wellington", Rails.application.config.time_zone
end
test "raises when an invalid timezone is defined in the config" do
@@ -577,6 +567,54 @@ module ApplicationTests
assert_equal 'permitted', last_response.body
end
+ test "config.action_controller.action_on_unpermitted_parameters = :raise" do
+ app_file 'app/controllers/posts_controller.rb', <<-RUBY
+ class PostsController < ActionController::Base
+ def create
+ render text: params.require(:post).permit(:name)
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ routes.prepend do
+ resources :posts
+ end
+ config.action_controller.action_on_unpermitted_parameters = :raise
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters
+
+ post "/posts", {post: {"title" =>"zomg"}}
+ assert_match "We're sorry, but something went wrong", last_response.body
+ end
+
+ test "config.action_controller.action_on_unpermitted_parameters is :log by default on development" do
+ ENV["RAILS_ENV"] = "development"
+
+ require "#{app_path}/config/environment"
+
+ assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters
+ end
+
+ test "config.action_controller.action_on_unpermitted_parameters is :log by defaul on test" do
+ ENV["RAILS_ENV"] = "test"
+
+ require "#{app_path}/config/environment"
+
+ assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters
+ end
+
+ test "config.action_controller.action_on_unpermitted_parameters is false by default on production" do
+ ENV["RAILS_ENV"] = "production"
+
+ require "#{app_path}/config/environment"
+
+ assert_equal false, ActionController::Parameters.action_on_unpermitted_parameters
+ end
+
test "config.action_dispatch.ignore_accept_header" do
make_basic_app do |app|
app.config.action_dispatch.ignore_accept_header = true
diff --git a/railties/test/application/initializers/load_path_test.rb b/railties/test/application/initializers/load_path_test.rb
index 31811e7f92..595f58bd91 100644
--- a/railties/test/application/initializers/load_path_test.rb
+++ b/railties/test/application/initializers/load_path_test.rb
@@ -64,7 +64,7 @@ module ApplicationTests
add_to_config <<-RUBY
config.root = "#{app_path}"
- config.eager_load_paths << "#{app_path}/lib"
+ config.autoload_paths << "#{app_path}/lib"
RUBY
require "#{app_path}/config/environment"
diff --git a/railties/test/application/middleware/best_practices_test.rb b/railties/test/application/middleware/best_practices_test.rb
deleted file mode 100644
index f6783c6ca2..0000000000
--- a/railties/test/application/middleware/best_practices_test.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require 'isolation/abstract_unit'
-
-module ApplicationTests
- class BestPracticesTest < ActiveSupport::TestCase
- include ActiveSupport::Testing::Isolation
-
- def setup
- build_app
- boot_rails
- require 'rack/test'
- extend Rack::Test::Methods
- simple_controller
- end
-
- def teardown
- teardown_app
- end
-
- test "simple controller in production mode returns best standards" do
- get '/foo'
- assert_equal "IE=Edge,chrome=1", last_response.headers["X-UA-Compatible"]
- end
-
- test "simple controller in development mode leaves out Chrome" do
- app("development")
- get "/foo"
- assert_equal "IE=Edge", last_response.headers["X-UA-Compatible"]
- end
- end
-end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index c5a0d02fe1..73ef2046c0 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -45,8 +45,7 @@ module ApplicationTests
"ActionDispatch::ParamsParser",
"Rack::Head",
"Rack::ConditionalGet",
- "Rack::ETag",
- "ActionDispatch::BestStandardsSupport"
+ "Rack::ETag"
], middleware
end
diff --git a/railties/test/application/paths_test.rb b/railties/test/application/paths_test.rb
index 4029984ce9..f6a77a0d17 100644
--- a/railties/test/application/paths_test.rb
+++ b/railties/test/application/paths_test.rb
@@ -54,11 +54,11 @@ module ApplicationTests
assert_equal root("app", "controllers"), @paths["app/controllers"].expanded.first
end
- test "booting up Rails yields a list of paths that are eager" do
- eager_load = @paths.eager_load
- assert eager_load.include?(root("app/controllers"))
- assert eager_load.include?(root("app/helpers"))
- assert eager_load.include?(root("app/models"))
+ test "booting up Rails yields a list of paths that will be eager loaded" do
+ autoload_paths = @paths.autoload_paths
+ assert autoload_paths.include?(root("app/controllers"))
+ assert autoload_paths.include?(root("app/helpers"))
+ assert autoload_paths.include?(root("app/models"))
end
test "environments has a glob equal to the current environment" do
diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb
index f65b5e2f2d..6595c40f8b 100644
--- a/railties/test/application/runner_test.rb
+++ b/railties/test/application/runner_test.rb
@@ -37,27 +37,27 @@ module ApplicationTests
end
def test_should_run_file
- app_file "script/count_users.rb", <<-SCRIPT
+ app_file "bin/count_users.rb", <<-SCRIPT
puts User.count
SCRIPT
- assert_match "42", Dir.chdir(app_path) { `bundle exec rails runner "script/count_users.rb"` }
+ assert_match "42", Dir.chdir(app_path) { `bundle exec rails runner "bin/count_users.rb"` }
end
def test_should_set_dollar_0_to_file
- app_file "script/dollar0.rb", <<-SCRIPT
+ app_file "bin/dollar0.rb", <<-SCRIPT
puts $0
SCRIPT
- assert_match "script/dollar0.rb", Dir.chdir(app_path) { `bundle exec rails runner "script/dollar0.rb"` }
+ assert_match "bin/dollar0.rb", Dir.chdir(app_path) { `bundle exec rails runner "bin/dollar0.rb"` }
end
def test_should_set_dollar_program_name_to_file
- app_file "script/program_name.rb", <<-SCRIPT
+ app_file "bin/program_name.rb", <<-SCRIPT
puts $PROGRAM_NAME
SCRIPT
- assert_match "script/program_name.rb", Dir.chdir(app_path) { `bundle exec rails runner "script/program_name.rb"` }
+ assert_match "bin/program_name.rb", Dir.chdir(app_path) { `bundle exec rails runner "bin/program_name.rb"` }
end
def test_with_hook
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index ee93dc49cd..83f43d1025 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -70,7 +70,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
def test_application_new_exits_with_non_zero_code_on_invalid_application_name
- quietly { system 'rails new test' }
+ quietly { system 'rails new test --no-rc' }
assert_equal false, $?.success?
end
@@ -164,6 +164,11 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_config_database_app_name_with_period
+ run_generator [File.join(destination_root, "common.usage.com"), "-d", "postgresql"]
+ assert_file "common.usage.com/config/database.yml", /common_usage_com/
+ end
+
def test_config_postgresql_database
run_generator([destination_root, "-d", "postgresql"])
assert_file "config/database.yml", /postgresql/
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index c34ce285e3..013cb78252 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -39,6 +39,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
assert_instance_method :destroy, content do |m|
assert_match(/@user\.destroy/, m)
+ assert_match(/User was successfully destroyed/, m)
end
assert_instance_method :set_user, content do |m|
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 7ae1b5ccfd..68d96bae94 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -279,7 +279,7 @@ Module.new do
environment = File.expand_path('../../../../load_paths', __FILE__)
require_environment = "-r #{environment}"
- `#{Gem.ruby} #{require_environment} #{RAILS_FRAMEWORK_ROOT}/railties/bin/rails new #{app_template_path} --skip-gemfile`
+ `#{Gem.ruby} #{require_environment} #{RAILS_FRAMEWORK_ROOT}/railties/bin/rails new #{app_template_path} --skip-gemfile --no-rc`
File.open("#{app_template_path}/config/boot.rb", 'w') do |f|
f.puts "require '#{environment}'"
f.puts "require 'rails/all'"
diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb
index 12f18b9dbf..6f860469fd 100644
--- a/railties/test/paths_test.rb
+++ b/railties/test/paths_test.rb
@@ -135,56 +135,48 @@ class PathsTest < ActiveSupport::TestCase
assert_equal 2, @root.autoload_once.size
end
- test "it is possible to mark a path as eager loaded" do
+ test "marking a path as eager loaded is deprecated" do
@root["app"] = "/app"
- @root["app"].eager_load!
- assert @root["app"].eager_load?
- assert @root.eager_load.include?(@root["app"].to_a.first)
+ assert_deprecated{ @root["app"].eager_load! }
+ assert_deprecated{ assert @root["app"].eager_load? }
+ assert_deprecated{ assert @root.eager_load.include?(@root["app"].to_a.first) }
end
- test "it is possible to skip a path from eager loading" do
+ test "skipping a path from eager loading is deprecated" do
@root["app"] = "/app"
- @root["app"].eager_load!
- assert @root["app"].eager_load?
+ assert_deprecated{ @root["app"].eager_load! }
+ assert_deprecated{ assert @root["app"].eager_load? }
- @root["app"].skip_eager_load!
- assert !@root["app"].eager_load?
- assert !@root.eager_load.include?(@root["app"].to_a.first)
+ assert_deprecated{ @root["app"].skip_eager_load! }
+ assert_deprecated{ assert !@root["app"].eager_load? }
+ assert_deprecated{ assert !@root.eager_load.include?(@root["app"].to_a.first) }
end
- test "it is possible to add a path without assignment and mark it as eager" do
- @root.add "app", with: "/app", eager_load: true
- assert @root["app"].eager_load?
- assert @root.eager_load.include?("/app")
+ test "adding a path with eager_load option is deprecated" do
+ assert_deprecated{ @root.add "app", with: "/app", eager_load: true }
+ assert_deprecated{ assert @root["app"].eager_load? }
+ assert_deprecated{ assert @root.eager_load.include?("/app") }
end
- test "it is possible to add multiple paths without assignment and mark them as eager" do
- @root.add "app", with: ["/app", "/app2"], eager_load: true
- assert @root["app"].eager_load?
- assert @root.eager_load.include?("/app")
- assert @root.eager_load.include?("/app2")
- end
-
- test "it is possible to create a path without assignment and mark it both as eager and load once" do
- @root.add "app", with: "/app", eager_load: true, autoload_once: true
- assert @root["app"].eager_load?
- assert @root["app"].autoload_once?
- assert @root.eager_load.include?("/app")
- assert @root.autoload_once.include?("/app")
+ test "adding multiple paths with eager_load option is deprecated" do
+ assert_deprecated{ @root.add "app", with: ["/app", "/app2"], eager_load: true }
+ assert_deprecated{ assert @root["app"].eager_load? }
+ assert_deprecated{ assert @root.eager_load.include?("/app") }
+ assert_deprecated{ assert @root.eager_load.include?("/app2") }
end
test "making a path eager more than once only includes it once in @root.eager_paths" do
@root["app"] = "/app"
- @root["app"].eager_load!
- @root["app"].eager_load!
- assert_equal 1, @root.eager_load.select {|p| p == @root["app"].expanded.first }.size
+ assert_deprecated{ @root["app"].eager_load! }
+ assert_deprecated{ @root["app"].eager_load! }
+ assert_deprecated{ assert_equal 1, @root.eager_load.select {|p| p == @root["app"].expanded.first }.size }
end
test "paths added to a eager_load path should be added to the eager_load collection" do
@root["app"] = "/app"
- @root["app"].eager_load!
+ assert_deprecated{ @root["app"].eager_load! }
@root["app"] << "/app2"
- assert_equal 2, @root.eager_load.size
+ assert_deprecated{ assert_equal 2, @root.eager_load.size }
end
test "it should be possible to add a path's default glob" do
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index a4a75fe459..37d0be107c 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -1241,6 +1241,12 @@ YAML
assert_equal '/foo/bukkits/bukkit', last_response.body
end
+ test "engines method is properly deprecated" do
+ boot_rails
+
+ assert_deprecated { app.railties.engines }
+ end
+
private
def app
Rails.application
diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb
index a1c52f01dc..80559a6e36 100644
--- a/railties/test/railties/mounted_engine_test.rb
+++ b/railties/test/railties/mounted_engine_test.rb
@@ -89,6 +89,7 @@ module ApplicationTests
get '/generate_application_route', to: 'posts#generate_application_route'
get '/application_route_in_view', to: 'posts#application_route_in_view'
get '/engine_polymorphic_path', to: 'posts#engine_polymorphic_path'
+ get '/engine_asset_path', to: 'posts#engine_asset_path'
end
RUBY
@@ -113,6 +114,10 @@ module ApplicationTests
def engine_polymorphic_path
render text: polymorphic_path(Post.new)
end
+
+ def engine_asset_path
+ render inline: "<%= asset_path 'images/foo.png' %>"
+ end
end
end
RUBY
@@ -211,6 +216,10 @@ module ApplicationTests
# and in an application
get "/application_polymorphic_path"
assert_equal "/posts/44", last_response.body
+
+ # test that asset path will not get script_name when generated in the engine
+ get "/someone/blog/engine_asset_path"
+ assert_equal "/images/foo.png", last_response.body
end
test "route path for controller action when engine is mounted at root" do