aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--Gemfile5
-rw-r--r--actionmailer/CHANGELOG.md5
-rw-r--r--actionmailer/README.rdoc2
-rw-r--r--actionmailer/lib/action_mailer/base.rb4
-rw-r--r--actionmailer/lib/action_mailer/collector.rb2
-rw-r--r--actionmailer/lib/action_mailer/test_helper.rb12
-rw-r--r--actionmailer/lib/rails/generators/mailer/USAGE5
-rw-r--r--actionpack/CHANGELOG.md181
-rw-r--r--actionpack/README.rdoc2
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb2
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/caching.rb14
-rw-r--r--actionpack/lib/action_controller/deprecated/performance_test.rb3
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb2
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb2
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb10
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb3
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb8
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb181
-rw-r--r--actionpack/lib/action_controller/railtie.rb19
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb23
-rw-r--r--actionpack/lib/action_controller/test_case.rb3
-rw-r--r--actionpack/lib/action_dispatch.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.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/request.rb19
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb4
-rw-r--r--actionpack/lib/action_dispatch/journey/formatter.rb7
-rw-r--r--actionpack/lib/action_dispatch/journey/route.rb52
-rw-r--r--actionpack/lib/action_dispatch/journey/router.rb6
-rw-r--r--actionpack/lib/action_dispatch/journey/routes.rb27
-rw-r--r--actionpack/lib/action_dispatch/middleware/best_standards_support.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb31
-rw-r--r--actionpack/lib/action_dispatch/middleware/exception_wrapper.rb5
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb22
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb8
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb40
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb17
-rw-r--r--actionpack/lib/action_dispatch/middleware/ssl.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb3
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb12
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb24
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/missing_template.erb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb24
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb12
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/routes/_route_wrapper.html.erb56
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb144
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb112
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb268
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb8
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb9
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/performance_test.rb10
-rw-r--r--actionpack/lib/action_view/base.rb2
-rw-r--r--actionpack/lib/action_view/digestor.rb21
-rw-r--r--actionpack/lib/action_view/helpers.rb5
-rw-r--r--actionpack/lib/action_view/helpers/asset_url_helper.rb3
-rw-r--r--actionpack/lib/action_view/helpers/benchmark_helper.rb13
-rw-r--r--actionpack/lib/action_view/helpers/cache_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb6
-rw-r--r--actionpack/lib/action_view/helpers/debug_helper.rb14
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb70
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb16
-rw-r--r--actionpack/lib/action_view/helpers/record_tag_helper.rb4
-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/renderer/partial_renderer.rb2
-rw-r--r--actionpack/lib/action_view/template.rb1
-rw-r--r--actionpack/lib/action_view/template/resolver.rb2
-rw-r--r--actionpack/lib/action_view/template/types.rb1
-rw-r--r--actionpack/test/abstract/abstract_controller_test.rb8
-rw-r--r--actionpack/test/abstract/helper_test.rb10
-rw-r--r--actionpack/test/abstract/translation_test.rb81
-rw-r--r--actionpack/test/activerecord/form_helper_activerecord_test.rb91
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb2
-rw-r--r--actionpack/test/controller/caching_test.rb18
-rw-r--r--actionpack/test/controller/filters_test.rb8
-rw-r--r--actionpack/test/controller/integration_test.rb58
-rw-r--r--actionpack/test/controller/layout_test.rb2
-rw-r--r--actionpack/test/controller/localized_templates_test.rb8
-rw-r--r--actionpack/test/controller/output_escaping_test.rb2
-rw-r--r--actionpack/test/controller/parameters/log_on_unpermitted_params_test.rb50
-rw-r--r--actionpack/test/controller/parameters/nested_parameters_test.rb56
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb125
-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/render_test.rb38
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb2
-rw-r--r--actionpack/test/controller/required_params_test.rb15
-rw-r--r--actionpack/test/controller/test_case_test.rb33
-rw-r--r--actionpack/test/controller/url_for_test.rb5
-rw-r--r--actionpack/test/controller/webservice_test.rb50
-rw-r--r--actionpack/test/dispatch/best_standards_support_test.rb3
-rw-r--r--actionpack/test/dispatch/debug_exceptions_test.rb28
-rw-r--r--actionpack/test/dispatch/request/json_params_parsing_test.rb22
-rw-r--r--actionpack/test/dispatch/request/query_string_parsing_test.rb4
-rw-r--r--actionpack/test/dispatch/request/xml_params_parsing_test.rb17
-rw-r--r--actionpack/test/dispatch/request_test.rb25
-rw-r--r--actionpack/test/dispatch/routing/inspector_test.rb36
-rw-r--r--actionpack/test/dispatch/routing_test.rb85
-rw-r--r--actionpack/test/dispatch/ssl_test.rb7
-rw-r--r--actionpack/test/fixtures/developer.rb1
-rw-r--r--actionpack/test/fixtures/test/_partial_name_local_variable.erb1
-rw-r--r--actionpack/test/journey/route_test.rb13
-rw-r--r--actionpack/test/journey/router_test.rb8
-rw-r--r--actionpack/test/template/atom_feed_helper_test.rb2
-rw-r--r--actionpack/test/template/benchmark_helper_test.rb24
-rw-r--r--actionpack/test/template/date_helper_i18n_test.rb11
-rw-r--r--actionpack/test/template/date_helper_test.rb132
-rw-r--r--actionpack/test/template/digestor_test.rb18
-rw-r--r--actionpack/test/template/form_helper_test.rb779
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb4
-rw-r--r--actionpack/test/template/render_test.rb7
-rw-r--r--activemodel/README.rdoc22
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb2
-rw-r--r--activemodel/lib/active_model/dirty.rb2
-rw-r--r--activemodel/lib/active_model/validations/clusivity.rb2
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb4
-rw-r--r--activemodel/lib/active_model/validator.rb2
-rw-r--r--activemodel/test/cases/conversion_test.rb4
-rw-r--r--activemodel/test/cases/dirty_test.rb13
-rw-r--r--activemodel/test/cases/errors_test.rb53
-rw-r--r--activemodel/test/cases/model_test.rb6
-rw-r--r--activemodel/test/models/helicopter.rb4
-rw-r--r--activerecord/CHANGELOG.md37
-rw-r--r--activerecord/Rakefile5
-rw-r--r--activerecord/activerecord.gemspec2
-rw-r--r--activerecord/lib/active_record/aggregations.rb2
-rw-r--r--activerecord/lib/active_record/associations.rb18
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb1
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb7
-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.rb5
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb6
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb1
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb2
-rw-r--r--activerecord/lib/active_record/base.rb50
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb76
-rw-r--r--activerecord/lib/active_record/core.rb23
-rw-r--r--activerecord/lib/active_record/fixtures.rb2
-rw-r--r--activerecord/lib/active_record/inheritance.rb2
-rw-r--r--activerecord/lib/active_record/locking/pessimistic.rb2
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb12
-rw-r--r--activerecord/lib/active_record/persistence.rb26
-rw-r--r--activerecord/lib/active_record/relation.rb12
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb2
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb1
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb8
-rw-r--r--activerecord/lib/active_record/scoping/named.rb4
-rw-r--r--activerecord/lib/active_record/store.rb6
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb4
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb38
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb7
-rw-r--r--activerecord/lib/active_record/tasks/sqlite_database_tasks.rb6
-rw-r--r--activerecord/lib/active_record/test_case.rb7
-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.rb12
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_test.rb12
-rw-r--r--activerecord/test/cases/adapters/postgresql/active_schema_test.rb1
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb30
-rw-r--r--activerecord/test/cases/adapters/postgresql/ltree_test.rb41
-rw-r--r--activerecord/test/cases/ar_schema_test.rb3
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb12
-rw-r--r--activerecord/test/cases/associations/eager_test.rb14
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb24
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb4
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb9
-rw-r--r--activerecord/test/cases/base_test.rb12
-rw-r--r--activerecord/test/cases/column_test.rb26
-rw-r--r--activerecord/test/cases/connection_pool_test.rb14
-rw-r--r--activerecord/test/cases/deprecated_dynamic_methods_test.rb6
-rw-r--r--activerecord/test/cases/dup_test.rb26
-rw-r--r--activerecord/test/cases/explain_test.rb2
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb42
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb6
-rw-r--r--activerecord/test/cases/migration_test.rb20
-rw-r--r--activerecord/test/cases/migrator_test.rb1
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb4
-rw-r--r--activerecord/test/cases/persistence_test.rb4
-rw-r--r--activerecord/test/cases/relation/where_test.rb4
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb24
-rw-r--r--activerecord/test/cases/relations_test.rb11
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb7
-rw-r--r--activerecord/test/cases/store_test.rb7
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb14
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb10
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb10
-rw-r--r--activerecord/test/cases/transactions_test.rb28
-rw-r--r--activerecord/test/models/developer.rb9
-rw-r--r--activerecord/test/models/post.rb17
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb11
-rw-r--r--activesupport/CHANGELOG.md45
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb3
-rw-r--r--activesupport/lib/active_support/cache.rb66
-rw-r--r--activesupport/lib/active_support/core_ext/array/access.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date/conversions.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/date/infinite_comparable.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/date_time.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/calculations.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb27
-rw-r--r--activesupport/lib/active_support/core_ext/infinite_comparable.rb35
-rw-r--r--activesupport/lib/active_support/core_ext/kernel/reporting.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/marshal.rb24
-rw-r--r--activesupport/lib/active_support/core_ext/numeric.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/object/acts_like.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/object/instance_variables.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb59
-rw-r--r--activesupport/lib/active_support/core_ext/time.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/time/infinite_comparable.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/time/zones.rb2
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb32
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb14
-rw-r--r--activesupport/lib/active_support/json/encoding.rb2
-rw-r--r--activesupport/lib/active_support/locale/en.yml6
-rw-r--r--activesupport/lib/active_support/log_subscriber.rb4
-rw-r--r--activesupport/lib/active_support/log_subscriber/test_helper.rb2
-rw-r--r--activesupport/lib/active_support/notifications/instrumenter.rb23
-rw-r--r--activesupport/lib/active_support/railtie.rb30
-rw-r--r--activesupport/lib/active_support/testing/assertions.rb2
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb68
-rw-r--r--activesupport/lib/active_support/testing/performance.rb271
-rw-r--r--activesupport/lib/active_support/testing/performance/jruby.rb115
-rw-r--r--activesupport/lib/active_support/testing/performance/rubinius.rb113
-rw-r--r--activesupport/lib/active_support/testing/performance/ruby.rb173
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb18
-rw-r--r--activesupport/test/caching_test.rb6
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb13
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb20
-rw-r--r--activesupport/test/core_ext/duration_test.rb14
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb30
-rw-r--r--activesupport/test/core_ext/numeric_ext_test.rb74
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb31
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb54
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb13
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb16
-rw-r--r--activesupport/test/dependencies_test.rb10
-rw-r--r--activesupport/test/deprecation_test.rb7
-rw-r--r--activesupport/test/i18n_test.rb2
-rw-r--r--activesupport/test/inflector_test.rb2
-rw-r--r--activesupport/test/inflector_test_cases.rb1
-rw-r--r--activesupport/test/message_encryptor_test.rb4
-rw-r--r--activesupport/test/notifications/instrumenter_test.rb50
-rw-r--r--activesupport/test/test_test.rb36
-rw-r--r--activesupport/test/testing/performance_test.rb68
-rw-r--r--guides/assets/stylesheets/main.css6
-rw-r--r--guides/code/getting_started/README.rdoc263
-rw-r--r--guides/code/getting_started/doc/README_FOR_APP2
-rw-r--r--guides/code/getting_started/test/performance/browsing_test.rb12
-rw-r--r--guides/rails_guides/markdown.rb4
-rw-r--r--guides/source/4_0_release_notes.md4
-rw-r--r--guides/source/action_controller_overview.md12
-rw-r--r--guides/source/action_mailer_basics.md22
-rw-r--r--guides/source/active_record_basics.md4
-rw-r--r--guides/source/active_record_callbacks.md3
-rw-r--r--guides/source/active_record_querying.md4
-rw-r--r--guides/source/active_record_validations.md1
-rw-r--r--guides/source/active_support_core_extensions.md31
-rw-r--r--guides/source/asset_pipeline.md26
-rw-r--r--guides/source/caching_with_rails.md96
-rw-r--r--guides/source/command_line.md48
-rw-r--r--guides/source/configuring.md14
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md1
-rw-r--r--guides/source/credits.html.erb2
-rw-r--r--guides/source/debugging_rails_applications.md8
-rw-r--r--guides/source/documents.yaml5
-rw-r--r--guides/source/engines.md34
-rw-r--r--guides/source/form_helpers.md6
-rw-r--r--guides/source/generators.md18
-rw-r--r--guides/source/getting_started.md21
-rw-r--r--guides/source/i18n.md4
-rw-r--r--guides/source/initialization.md124
-rw-r--r--guides/source/layouts_and_rendering.md2
-rw-r--r--guides/source/nested_model_forms.md2
-rw-r--r--guides/source/performance_testing.md686
-rw-r--r--guides/source/rails_application_templates.md7
-rw-r--r--guides/source/rails_on_rack.md4
-rw-r--r--guides/source/security.md2
-rw-r--r--guides/source/testing.md6
-rw-r--r--guides/source/working_with_javascript_in_rails.md64
-rw-r--r--install.rb5
-rw-r--r--rails.gemspec2
-rw-r--r--railties/CHANGELOG.md78
-rw-r--r--railties/lib/rails/app_rails_loader.rb29
-rw-r--r--railties/lib/rails/application/configuration.rb4
-rw-r--r--railties/lib/rails/cli.rb7
-rw-r--r--railties/lib/rails/commands.rb13
-rw-r--r--railties/lib/rails/commands/benchmarker.rb34
-rw-r--r--railties/lib/rails/commands/console.rb24
-rw-r--r--railties/lib/rails/commands/dbconsole.rb10
-rw-r--r--railties/lib/rails/commands/profiler.rb32
-rw-r--r--railties/lib/rails/commands/runner.rb2
-rw-r--r--railties/lib/rails/engine.rb6
-rw-r--r--railties/lib/rails/generators.rb2
-rw-r--r--railties/lib/rails/generators/actions.rb6
-rw-r--r--railties/lib/rails/generators/active_model.rb8
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/index.html.erb12
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb35
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/README259
-rw-r--r--railties/lib/rails/generators/rails/app/templates/README.rdoc28
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/mailers/.empty_directory0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/models/.empty_directory0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/bundle3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/rails3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/rake3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb17
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/doc/README_FOR_APP2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/script/rails5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/fixtures/.empty_directory0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/functional/.empty_directory0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/integration/.empty_directory0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb12
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/unit/.empty_directory0
-rw-r--r--railties/lib/rails/generators/rails/controller/USAGE2
-rw-r--r--railties/lib/rails/generators/rails/migration/USAGE13
-rw-r--r--railties/lib/rails/generators/rails/model/USAGE14
-rw-r--r--railties/lib/rails/generators/rails/performance_test/USAGE10
-rw-r--r--railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb7
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb32
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/Gemfile2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/Rakefile2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/bin/rails.tt (renamed from railties/lib/rails/generators/rails/plugin_new/templates/script/rails.tt)0
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%.rb2
-rw-r--r--railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb7
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb9
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb72
-rw-r--r--railties/lib/rails/generators/test_unit/performance/performance_generator.rb13
-rw-r--r--railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb12
-rw-r--r--railties/lib/rails/info.rb2
-rw-r--r--railties/lib/rails/info_controller.rb6
-rw-r--r--railties/lib/rails/paths.rb10
-rw-r--r--railties/lib/rails/performance_test_help.rb3
-rw-r--r--railties/lib/rails/rack/logger.rb41
-rw-r--r--railties/lib/rails/railtie.rb2
-rw-r--r--railties/lib/rails/script_rails_loader.rb29
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb51
-rw-r--r--railties/lib/rails/tasks/documentation.rake7
-rw-r--r--railties/lib/rails/tasks/framework.rake10
-rw-r--r--railties/lib/rails/tasks/log.rake22
-rw-r--r--railties/lib/rails/tasks/routes.rake4
-rw-r--r--railties/lib/rails/templates/layouts/application.html.erb2
-rw-r--r--railties/lib/rails/templates/rails/info/routes.html.erb5
-rw-r--r--railties/lib/rails/templates/rails/welcome/index.html.erb25
-rw-r--r--railties/lib/rails/test_help.rb10
-rw-r--r--railties/lib/rails/test_unit/railtie.rb1
-rw-r--r--railties/lib/rails/test_unit/testing.rake13
-rw-r--r--railties/test/app_rails_loader_test.rb41
-rw-r--r--railties/test/application/configuration_test.rb70
-rw-r--r--railties/test/application/generators_test.rb2
-rw-r--r--railties/test/application/middleware/cache_test.rb12
-rw-r--r--railties/test/application/middleware/static_test.rb31
-rw-r--r--railties/test/application/rake/notes_test.rb47
-rw-r--r--railties/test/application/runner_test.rb12
-rw-r--r--railties/test/application/test_test.rb25
-rw-r--r--railties/test/commands/console_test.rb6
-rw-r--r--railties/test/commands/dbconsole_test.rb12
-rw-r--r--railties/test/generators/actions_test.rb6
-rw-r--r--railties/test/generators/app_generator_test.rb12
-rw-r--r--railties/test/generators/performance_test_generator_test.rb12
-rw-r--r--railties/test/generators/plugin_new_generator_test.rb12
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb16
-rw-r--r--railties/test/generators/scaffold_generator_test.rb9
-rw-r--r--railties/test/generators/shared_generator_tests.rb6
-rw-r--r--railties/test/isolation/abstract_unit.rb2
-rw-r--r--railties/test/rack_logger_test.rb71
-rw-r--r--railties/test/railties/mounted_engine_test.rb9
-rw-r--r--railties/test/script_rails_loader_test.rb22
403 files changed, 4864 insertions, 4509 deletions
diff --git a/.travis.yml b/.travis.yml
index 18b0100c62..ba5526c009 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -24,6 +24,3 @@ notifications:
rooms:
- secure: "YA1alef1ESHWGFNVwvmVGCkMe4cUy4j+UcNvMUESraceiAfVyRMAovlQBGs6\n9kBRm7DHYBUXYC2ABQoJbQRLDr/1B5JPf/M8+Qd7BKu8tcDC03U01SMHFLpO\naOs/HLXcDxtnnpL07tGVsm0zhMc5N8tq4/L3SHxK7Vi+TacwQzI="
bundler_args: --path vendor/bundle
-matrix:
- allow_failures:
- - rvm: 2.0.0
diff --git a/Gemfile b/Gemfile
index 94a0d15d16..122c108a07 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,11 +8,12 @@ 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'
-gem 'thread_safe', '~> 0.1'
+# TODO: Release thor
+gem 'thor', github: 'wycats/thor', branch: 'master'
gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders', branch: 'master'
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index 9feca324a3..43bfa536c1 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,5 +1,10 @@
## Rails 4.0.0 (unreleased) ##
+* Eager loading made to use relation's in_clause_length instead of host's one.
+ Fix #8474
+
+ *Boris Staal*
+
* Explicit multipart messages no longer set the order of the MIME parts.
*Nate Berkopec*
diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc
index 59c33b7940..9feb2add5b 100644
--- a/actionmailer/README.rdoc
+++ b/actionmailer/README.rdoc
@@ -98,7 +98,7 @@ Example:
class Mailman < ActionMailer::Base
def receive(email)
- page = Page.find_by_address(email.to.first)
+ page = Page.find_by(address: email.to.first)
page.emails.create(
subject: email.subject, body: email.body
)
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index a3c0b2c6bd..9ba606c045 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -530,7 +530,7 @@ module ActionMailer
# The resulting Mail::Message will have the following in its header:
#
# X-Special-Domain-Specific-Header: SecretValue
- def headers(args=nil)
+ def headers(args = nil)
if args
@_message.headers(args)
else
@@ -660,7 +660,7 @@ module ActionMailer
# format.html
# end
#
- def mail(headers={}, &block)
+ def mail(headers = {}, &block)
@_mail_was_called = true
m = @_message
diff --git a/actionmailer/lib/action_mailer/collector.rb b/actionmailer/lib/action_mailer/collector.rb
index cbb2778fae..e8883a8235 100644
--- a/actionmailer/lib/action_mailer/collector.rb
+++ b/actionmailer/lib/action_mailer/collector.rb
@@ -20,7 +20,7 @@ module ActionMailer
end
alias :all :any
- def custom(mime, options={})
+ def custom(mime, options = {})
options.reverse_merge!(content_type: mime.to_s)
@context.formats = [mime.to_sym]
options[:body] = block_given? ? yield : @default_render.call
diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb
index 3ab37d8142..6452bf616c 100644
--- a/actionmailer/lib/action_mailer/test_helper.rb
+++ b/actionmailer/lib/action_mailer/test_helper.rb
@@ -4,9 +4,9 @@ module ActionMailer
#
# def test_emails
# assert_emails 0
- # ContactMailer.deliver_contact
+ # ContactMailer.welcome.deliver
# assert_emails 1
- # ContactMailer.deliver_contact
+ # ContactMailer.welcome.deliver
# assert_emails 2
# end
#
@@ -15,12 +15,12 @@ module ActionMailer
#
# def test_emails_again
# assert_emails 1 do
- # ContactMailer.deliver_contact
+ # ContactMailer.welcome.deliver
# end
#
# assert_emails 2 do
- # ContactMailer.deliver_contact
- # ContactMailer.deliver_contact
+ # ContactMailer.welcome.deliver
+ # ContactMailer.welcome.deliver
# end
# end
def assert_emails(number)
@@ -38,7 +38,7 @@ module ActionMailer
#
# def test_emails
# assert_no_emails
- # ContactMailer.deliver_contact
+ # ContactMailer.welcome.deliver
# assert_emails 1
# end
#
diff --git a/actionmailer/lib/rails/generators/mailer/USAGE b/actionmailer/lib/rails/generators/mailer/USAGE
index 7470289fd3..323bb8a87f 100644
--- a/actionmailer/lib/rails/generators/mailer/USAGE
+++ b/actionmailer/lib/rails/generators/mailer/USAGE
@@ -10,9 +10,8 @@ Example:
========
rails generate mailer Notifications signup forgot_password invoice
- creates a Notifications mailer class, views, test, and fixtures:
+ creates a Notifications mailer class, views, and test:
Mailer: app/mailers/notifications.rb
- Views: app/views/notifications/signup.erb [...]
+ Views: app/views/notifications/signup.text.erb [...]
Test: test/mailers/notifications_test.rb
- Fixtures: test/fixtures/notifications/signup [...]
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 3e8441343f..a35caf6a94 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,104 @@
## Rails 4.0.0 (unreleased) ##
+* 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*
+
+* `BestStandardsSupport` no longer duplicates `X-UA-Compatible` values on
+ each request to prevent header size from blowing up.
+
+ *Edward Anderson*
+
+* 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:
+
+ resources :posts, bucket_type: 'posts'
+
+ will be required whenever constructing the url from a hash such as a functional
+ test or using url_for directly. However using the explicit form alters the
+ behavior so it's not required:
+
+ resources :projects, defaults: { bucket_type: 'projects' }
+
+ This changes existing behavior slightly in that any routes which only differ
+ in their defaults will match the first route rather than the closest match.
+
+ *Andrew White*
+
+* Add support for routing constraints other than Regexp and String.
+ For example this now allows the use of arrays like this:
+
+ get '/foo/:action', to: 'foo', constraints: { subdomain: %w[www admin] }
+
+ or constraints where the request method returns an Fixnum like this:
+
+ get '/foo', to: 'foo#index', constraints: { port: 8080 }
+
+ Note that this only applies to constraints on the request - path constraints
+ still need to be specified as Regexps as the various constraints are compiled
+ into a single Regexp.
+
+ *Andrew White*
+
+* Fix a bug in integration tests where setting the port via a url passed to
+ the process method was ignored when constructing the request environment.
+
+ *Andrew White*
+
+* Allow `:selected` to be set on `date_select` tag helper.
+
+ *Colin Burn-Murdoch*
+
+* Fixed json params parsing regression for non-object JSON content.
+
+ *Dylan Smith*
+
+* Extract `ActionDispatch::PerformanceTest` into https://github.com/rails/rails-perftest
+ You can add the gem to your Gemfile to keep using performance tests.
+
+ gem 'rails-perftest'
+
+ *Yves Senn*
+
+* Added view_cache_dependency API for declaring dependencies that affect
+ cache digest computation.
+
+ *Jamis Buck*
+
+* `image_submit_tag` will set `alt` attribute from image source if not
+ specified.
+
+ *Nihad Abbasov*
+
+* Do not generate local variables for partials without object or collection.
+ Previously rendering a partial without giving `:object` or `:collection`
+ would generate a local variable with the partial name by default.
+
+ *Carlos Antonio da Silva*
+
* Return the last valid, non-private IP address from the X-Forwarded-For,
Client-IP and Remote-Addr headers, in that order. Document the rationale
for that decision, and describe the options that can be passed to the
@@ -145,7 +244,7 @@
*Drew Ulmer*
-* No sort Hash options in `grouped_options_for_select`. *Sergey Kojin*
+* Do not sort Hash options in `grouped_options_for_select`. *Sergey Kojin*
* Accept symbols as `send_data :disposition` value *Elia Schito*
@@ -193,14 +292,15 @@
* Render every partial with a new `ActionView::PartialRenderer`. This resolves
issues when rendering nested partials.
- Fix #8197
+ Fix #8197.
*Yves Senn*
* Introduce `ActionView::Template::Handlers::ERB.escape_whitelist`. This is a list
of mime types where template text is not html escaped by default. It prevents `Jack & Joe`
from rendering as `Jack &amp; Joe` for the whitelisted mime types. The default whitelist
- contains text/plain. Fix #7976
+ contains `text/plain`.
+ Fix #7976.
*Joost Baaij*
@@ -216,12 +316,13 @@
check_box("post", "comment_ids", { multiple: true, index: "foo" }, 1)
#=> <input name=\"post[foo][comment_ids][]\" type=\"hidden\" value=\"0\" /><input id=\"post_foo_comment_ids_1\" name=\"post[foo][comment_ids][]\" type=\"checkbox\" value=\"1\" />
- Fix #8108
+ Fix #8108.
*Daniel Fox, Grant Hutchins & Trace Wax*
* `BestStandardsSupport` middleware now appends it's `X-UA-Compatible` value to app's
- returned value if any. Fix #8086
+ returned value if any.
+ Fix #8086.
*Nikita Afanasenko*
@@ -230,21 +331,21 @@
*Pavel Nikitin*
-* Only non-js/css under app/assets path will be included in default config.assets.precompile.
+* Only non-js/css under `app/assets` path will be included in default `config.assets.precompile`.
*Josh Peek*
-* Remove support for the RAILS_ASSET_ID environment configuration
+* Remove support for the `RAILS_ASSET_ID` environment configuration
(no longer needed now that we have the asset pipeline).
*Josh Peek*
-* Remove old asset_path configuration (no longer needed now that we have the asset pipeline).
+* Remove old `asset_path` configuration (no longer needed now that we have the asset pipeline).
*Josh Peek*
* `assert_template` can be used to assert on the same template with different locals
- Fix #3675
+ Fix #3675.
*Yves Senn*
@@ -252,10 +353,10 @@
*Josh Peek*
-* Accept :remote as symbolic option for `link_to` helper. *Riley Lynch*
+* Accept `:remote` as symbolic option for `link_to` helper. *Riley Lynch*
* Warn when the `:locals` option is passed to `assert_template` outside of a view test case
- Fix #3415
+ Fix #3415.
*Yves Senn*
@@ -275,24 +376,24 @@
*Francesco Rodriguez*
-* Failsafe exception returns text/plain. *Steve Klabnik*
+* Failsafe exception returns `text/plain`. *Steve Klabnik*
* Remove `rack-cache` dependency from Action Pack and declare it on Gemfile
*Guillermo Iguaran*
-* Rename internal variables on ActionController::TemplateAssertions to prevent
- naming collisions. @partials, @templates and @layouts are now prefixed with an underscore.
- Fix #7459
+* Rename internal variables on `ActionController::TemplateAssertions` to prevent
+ naming collisions. `@partials`, `@templates` and `@layouts` are now prefixed with an underscore.
+ Fix #7459.
*Yves Senn*
-* `resource` and `resources` don't modify the passed options hash
- Fix #7777
+* `resource` and `resources` don't modify the passed options hash.
+ Fix #7777.
*Yves Senn*
-* Precompiled assets include aliases from foo.js to foo/index.js and vice versa.
+* Precompiled assets include aliases from `foo.js` to `foo/index.js` and vice versa.
# Precompiles phone-<digest>.css and aliases phone/index.css to phone.css.
config.assets.precompile = [ 'phone.css' ]
@@ -330,8 +431,8 @@
*Nihad Abbasov*
-* Deprecate Mime::Type#verify_request? and Mime::Type.browser_generated_types,
- since they are no longer used inside of Rails, they will be removed in Rails 4.1
+* Deprecate `Mime::Type#verify_request?` and `Mime::Type.browser_generated_types`,
+ since they are no longer used inside of Rails, they will be removed in Rails 4.1.
*Michael Grosser*
@@ -347,11 +448,12 @@
* Remove Integration between `attr_accessible`/`attr_protected` and
`ActionController::ParamsWrapper`. ParamWrapper now wraps all the parameters returned
- by the class method attribute_names
+ by the class method `attribute_names`.
*Guillermo Iguaran*
-* Fix #7646, the log now displays the correct status code when an exception is raised.
+* Log now displays the correct status code when an exception is raised.
+ Fix #7646.
*Yves Senn*
@@ -389,7 +491,7 @@
*Sergey Nartimov*
-* Add .ruby template handler, this handler simply allows arbitrary Ruby code as a template. *Guillermo Iguaran*
+* Add `.ruby` template handler, this handler simply allows arbitrary Ruby code as a template. *Guillermo Iguaran*
* Add `separator` option for `ActionView::Helpers::TextHelper#excerpt`:
@@ -410,17 +512,20 @@
end
end
-* Add automatic template digests to all `CacheHelper#cache` calls (originally spiked in the cache_digests plugin) *DHH*
+* Add automatic template digests to all `CacheHelper#cache` calls (originally spiked in the `cache_digests` plugin) *DHH*
* When building a URL fails, add missing keys provided by Journey. Failed URL
generation now returns a 500 status instead of a 404.
*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*
@@ -504,7 +609,7 @@
*Egor Homakov*
-* Allow data attributes to be set as a first-level option for form_for, so you can write `form_for @record, data: { behavior: 'autosave' }` instead of `form_for @record, html: { data: { behavior: 'autosave' } }` *DHH*
+* Allow data attributes to be set as a first-level option for `form_for`, so you can write `form_for @record, data: { behavior: 'autosave' }` instead of `form_for @record, html: { data: { behavior: 'autosave' } }` *DHH*
* Deprecate `button_to_function` and `link_to_function` helpers.
@@ -560,7 +665,9 @@
*Carlos Galdino + Rafael Mendonça França*
-* Show routes in exception page while debugging a `RoutingError` in development. *Richard Schneeman and Mattt Thompson*
+* Show routes in exception page while debugging a `RoutingError` in development.
+
+ *Richard Schneeman + Mattt Thompson + Yves Senn*
* Add `ActionController::Flash.add_flash_types` method to allow people to register their own flash types. e.g.:
@@ -610,7 +717,7 @@
*Sergey Nartimov*
-* change a way of ordering helpers from several directories. Previously,
+* Change a way of ordering helpers from several directories. Previously,
when loading helpers from multiple paths, all of the helpers files were
gathered into one array an then they were sorted. Helpers from different
directories should not be mixed before loading them to make loading more
@@ -646,7 +753,7 @@
* Add `time_field` and `time_field_tag` helpers which render an `input[type="time"]` tag. *Alex Soulim*
-* Removed old text_helper apis for highlight, excerpt and word_wrap *Jeremy Walker*
+* 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*
@@ -657,7 +764,7 @@
* Remove `:mouseover` option from `image_tag` helper. *Rafael Mendonça França*
-* The `select` method (select tag) forces :include_blank if `required` is true and
+* The `select` method (select tag) forces `:include_blank` if `required` is true and
`display size` is one and `multiple` is not true. *Angelo Capilleri*
* Copy literal route constraints to defaults so that url generation know about them.
@@ -679,7 +786,7 @@
* Make current object and counter (when it applies) variables accessible when
rendering templates with :object / :collection. *Carlos Antonio da Silva*
-* JSONP now uses mimetype text/javascript instead of application/json. *omjokine*
+* JSONP now uses mimetype `text/javascript` instead of `application/json`. *omjokine*
* Allow to lazy load `default_form_builder` by passing a `String` instead of a constant. *Piotr Sarnacki*
@@ -692,14 +799,14 @@
* Add `index` method to FormBuilder class. *Jorge Bejar*
-* Remove the leading \n added by textarea on assert_select. *Santiago Pastorino*
+* Remove the leading \n added by textarea on `assert_select`. *Santiago Pastorino*
* Changed default value for `config.action_view.embed_authenticity_token_in_remote_forms`
to `false`. This change breaks remote forms that need to work also without javascript,
so if you need such behavior, you can either set it to `true` or explicitly pass
- `authenticity_token: true` in form options
+ `authenticity_token: true` in form options.
-* Added ActionDispatch::SSL middleware that when included force all the requests to be under HTTPS protocol. *Rafael Mendonça França*
+* Added `ActionDispatch::SSL` middleware that when included force all the requests to be under HTTPS protocol. *Rafael Mendonça França*
* Add `include_hidden` option to select tag. With `include_hidden: false` select with `multiple` attribute doesn't generate hidden input with blank value. *Vasiliy Ermolovich*
diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc
index ccd0193515..29a7fcf0f0 100644
--- a/actionpack/README.rdoc
+++ b/actionpack/README.rdoc
@@ -10,7 +10,7 @@ It consists of several modules:
* Action Dispatch, which parses information about the web request, handles
routing as defined by the user, and does advanced processing related to HTTP
- such as MIME-type negotiation, decoding parameters in POST/PUT bodies,
+ such as MIME-type negotiation, decoding parameters in POST, PATCH, or PUT bodies,
handling HTTP caching logic, cookies and sessions.
* Action Controller, which provides a base controller class that can be
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index c65870cac6..f00fc46b2e 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
s.add_dependency 'activesupport', version
s.add_dependency 'builder', '~> 3.1.0'
- s.add_dependency 'rack', '~> 1.4.1'
+ s.add_dependency 'rack', '~> 1.4.3'
s.add_dependency 'rack-test', '~> 0.6.1'
s.add_dependency 'erubis', '~> 2.7.0'
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index 73799e8085..91864f2a35 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -209,7 +209,7 @@ module AbstractController
_write_layout_method
end
- delegate :_layout_conditions, :to => "self.class"
+ delegate :_layout_conditions, to: :class
module ClassMethods
def inherited(klass) # :nodoc:
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 1a13d7af29..9cacb3862b 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -42,7 +42,6 @@ module ActionController
autoload :Integration, 'action_controller/deprecated/integration_test'
autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
- autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
autoload :Routing, 'action_controller/deprecated'
autoload :TestCase, 'action_controller/test_case'
autoload :TemplateAssertions, 'action_controller/test_case'
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 2892e093af..cf2cda039d 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -70,12 +70,26 @@ module ActionController
config_accessor :perform_caching
self.perform_caching = true if perform_caching.nil?
+
+ class_attribute :_view_cache_dependencies
+ self._view_cache_dependencies = []
+ helper_method :view_cache_dependencies if respond_to?(:helper_method)
+ end
+
+ module ClassMethods
+ def view_cache_dependency(&dependency)
+ self._view_cache_dependencies += [dependency]
+ 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
+
protected
# Convenience accessor.
def cache(key, options = {}, &block)
diff --git a/actionpack/lib/action_controller/deprecated/performance_test.rb b/actionpack/lib/action_controller/deprecated/performance_test.rb
deleted file mode 100644
index c7ba5a2fe7..0000000000
--- a/actionpack/lib/action_controller/deprecated/performance_test.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-ActionController::PerformanceTest = ActionDispatch::PerformanceTest
-
-ActiveSupport::Deprecation.warn 'ActionController::PerformanceTest is deprecated and will be removed, use ActionDispatch::PerformanceTest instead.'
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 3f9b382a11..eddee08545 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/class/attribute'
-
module ActionController
module ConditionalGet
extend ActiveSupport::Concern
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 896238b7dc..e295002b16 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -228,7 +228,7 @@ module ActionController
end
def decode_credentials(header)
- HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
+ ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, '').split(',').map do |pair|
key, value = pair.split('=', 2)
[key.strip, value.to_s.gsub(/^"|"$/,'').delete('\'')]
end]
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 6bf306ac5b..d04fbae150 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -82,7 +82,7 @@ module ActionController #:nodoc:
# (by name) if it does not already exist, without web-services, it might look like this:
#
# def create
- # @company = Company.find_or_create_by_name(params[:company][:name])
+ # @company = Company.find_or_create_by(name: params[:company][:name])
# @person = @company.people.create(params[:person])
#
# redirect_to(person_list_url)
@@ -92,7 +92,7 @@ module ActionController #:nodoc:
#
# def create
# company = params[:person].delete(:company)
- # @company = Company.find_or_create_by_name(company[:name])
+ # @company = Company.find_or_create_by(name: company[:name])
# @person = @company.people.create(params[:person])
#
# respond_to do |format|
@@ -120,7 +120,7 @@ module ActionController #:nodoc:
# Note, however, the extra bit at the top of that action:
#
# company = params[:person].delete(:company)
- # @company = Company.find_or_create_by_name(company[:name])
+ # @company = Company.find_or_create_by(name: company[:name])
#
# This is because the incoming XML document (if a web-service request is in process) can only contain a
# single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
@@ -227,7 +227,7 @@ module ActionController #:nodoc:
# i.e. its +show+ action.
# 2. If there are validation errors, the response
# renders a default action, which is <tt>:new</tt> for a
- # +post+ request or <tt>:edit</tt> for +put+.
+ # +post+ request or <tt>:edit</tt> for +patch+ or +put+.
# Thus an example like this -
#
# respond_to :html, :xml
@@ -320,7 +320,7 @@ module ActionController #:nodoc:
# 2. <tt>:action</tt> - overwrites the default render action used after an
# unsuccessful html +post+ request.
def respond_with(*resources, &block)
- raise "In order to use respond_with, first you need to declare the formats your " <<
+ raise "In order to use respond_with, first you need to declare the formats your " \
"controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
if collector = retrieve_collector_from_mimes(&block)
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 091facfd8d..59b91a240e 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -32,7 +32,7 @@ module ActionController
# redirect_to :back
# redirect_to proc { edit_post_url(@post) }
#
- # The redirection happens as a "302 Moved" header unless otherwise specified.
+ # The redirection happens as a "302 Found" header unless otherwise specified.
#
# redirect_to post_url(@post), status: :found
# redirect_to action: 'atom', status: :moved_permanently
@@ -65,7 +65,6 @@ module ActionController
def redirect_to(options = {}, response_status = {}) #:doc:
raise ActionControllerError.new("Cannot redirect to nil!") unless options
raise AbstractController::DoubleRenderError if response_body
- logger.debug { "Redirected by #{caller(1).first rescue "unknown"}" } if logger
self.status = _extract_redirect_to_status(options, response_status)
self.location = _compute_redirect_to_location(options)
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 0b3c438ec2..73e9b5660d 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -26,7 +26,7 @@ module ActionController #:nodoc:
#
# class PostsController
# def index
- # @posts = Post.scoped
+ # @posts = Post.all
# render stream: true
# end
# end
@@ -51,9 +51,9 @@ module ActionController #:nodoc:
#
# def dashboard
# # Allow lazy execution of the queries
- # @posts = Post.scoped
- # @pages = Page.scoped
- # @articles = Article.scoped
+ # @posts = Post.all
+ # @pages = Page.all
+ # @articles = Article.all
# render stream: true
# end
#
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index da380dfbd8..0d84fbb62b 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -1,4 +1,3 @@
-require 'active_support/concern'
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/array/wrap'
require 'active_support/rescuable'
@@ -20,6 +19,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
@@ -44,10 +57,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
- # behaviour 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
@@ -57,6 +75,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>.
@@ -66,6 +94,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
@@ -151,6 +184,20 @@ 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+, or +IO+. 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({
@@ -197,32 +244,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)
+
params.permit!
end
@@ -301,6 +331,97 @@ 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,
+ ]
+
+ 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
+
+ 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] == []
+ # 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
@@ -374,12 +495,6 @@ module ActionController
extend ActiveSupport::Concern
include ActiveSupport::Rescuable
- included do
- rescue_from(ActionController::ParameterMissing) do |parameter_missing_exception|
- render text: "Required parameter missing: #{parameter_missing_exception.param}", status: :bad_request
- end
- end
-
# Returns a new ActionController::Parameters object that
# has been instantiated with the <tt>request.parameters</tt>.
def params
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 b49128c184..d598bac467 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -1,19 +1,30 @@
-require 'active_support/deprecation'
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::RecodIdentifier 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..d8206b573d 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -81,8 +81,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
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index b35761fb4a..9ab048b756 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -97,7 +97,6 @@ module ActionDispatch
autoload :Assertions
autoload :Integration
autoload :IntegrationTest, 'action_dispatch/testing/integration'
- autoload :PerformanceTest
autoload :TestProcess
autoload :TestRequest
autoload :TestResponse
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index 02ab49b44e..289e204ac8 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/object/duplicable'
+require 'action_dispatch/http/parameter_filter'
module ActionDispatch
module Http
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 6610315da7..25edd196c3 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -72,7 +72,7 @@ module ActionDispatch
end
end
- # Convert nested Hash to HashWithIndifferentAccess
+ # Convert nested Hash to ActiveSupport::HashWithIndifferentAccess
def normalize_parameters(value)
case value
when Hash
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index d60c8775af..7b04d6e851 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -1,12 +1,16 @@
-require 'tempfile'
require 'stringio'
-require 'strscan'
-require 'active_support/core_ext/hash/indifferent_access'
-require 'active_support/core_ext/string/access'
require 'active_support/inflector'
require 'action_dispatch/http/headers'
require 'action_controller/metal/exceptions'
+require 'rack/request'
+require 'action_dispatch/http/cache'
+require 'action_dispatch/http/mime_negotiation'
+require 'action_dispatch/http/parameters'
+require 'action_dispatch/http/filter_parameters'
+require 'action_dispatch/http/upload'
+require 'action_dispatch/http/url'
+require 'active_support/core_ext/array/conversions'
module ActionDispatch
class Request < Rack::Request
@@ -280,15 +284,14 @@ module ActionDispatch
LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip
end
- protected
-
# Remove nils from the params hash
def deep_munge(hash)
- hash.each_value do |v|
+ hash.each do |k, v|
case v
when Array
v.grep(Hash) { |x| deep_munge(x) }
v.compact!
+ hash[k] = nil if v.empty?
when Hash
deep_munge(v)
end
@@ -297,6 +300,8 @@ module ActionDispatch
hash
end
+ protected
+
def parse_query(qs)
deep_munge(super)
end
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index 79437d6e85..8a97248eb3 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -19,7 +19,7 @@ module ActionDispatch
# its interface is available directly.
attr_accessor :tempfile
- # TODO.
+ # A string with the headers of the multipart request.
attr_accessor :headers
def initialize(hash) # :nodoc:
@@ -75,7 +75,7 @@ module ActionDispatch
end
module Upload # :nodoc:
- # Convert nested Hash to HashWithIndifferentAccess and replace
+ # Convert nested Hash to ActiveSupport::HashWithIndifferentAccess and replace
# file upload hash with UploadedFile objects
def normalize_parameters(value)
if Hash === value && value.has_key?(:tempfile)
diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb
index cf755bfbeb..82c55660ea 100644
--- a/actionpack/lib/action_dispatch/journey/formatter.rb
+++ b/actionpack/lib/action_dispatch/journey/formatter.rb
@@ -1,3 +1,5 @@
+require 'action_controller/metal/exceptions'
+
module ActionDispatch
module Journey
# The Formatter class is used for formatting URLs. For example, parameters
@@ -27,7 +29,10 @@ module ActionDispatch
return [route.format(parameterized_parts), params]
end
- raise Router::RoutingError.new "missing required keys: #{missing_keys}"
+ message = "No route matches #{constraints.inspect}"
+ message << " missing required keys: #{missing_keys.inspect}" if name
+
+ raise ActionController::UrlGenerationError, message
end
def clear
diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb
index d18efd863a..063302e0f2 100644
--- a/actionpack/lib/action_dispatch/journey/route.rb
+++ b/actionpack/lib/action_dispatch/journey/route.rb
@@ -1,7 +1,7 @@
module ActionDispatch
module Journey # :nodoc:
class Route # :nodoc:
- attr_reader :app, :path, :verb, :defaults, :ip, :name
+ attr_reader :app, :path, :defaults, :name
attr_reader :constraints
alias :conditions :constraints
@@ -12,15 +12,11 @@ module ActionDispatch
# +path+ is a path constraint.
# +constraints+ is a hash of constraints to be applied to this route.
def initialize(name, app, path, constraints, defaults = {})
- constraints = constraints.dup
@name = name
@app = app
@path = path
- @verb = constraints[:request_method] || //
- @ip = constraints.delete(:ip) || //
@constraints = constraints
- @constraints.keep_if { |_,v| Regexp === v || String === v }
@defaults = defaults
@required_defaults = nil
@required_parts = nil
@@ -30,11 +26,11 @@ module ActionDispatch
end
def ast
- return @decorated_ast if @decorated_ast
-
- @decorated_ast = path.ast
- @decorated_ast.grep(Nodes::Terminal).each { |n| n.memo = self }
- @decorated_ast
+ @decorated_ast ||= begin
+ decorated_ast = path.ast
+ decorated_ast.grep(Nodes::Terminal).each { |n| n.memo = self }
+ decorated_ast
+ end
end
def requirements # :nodoc:
@@ -45,11 +41,11 @@ module ActionDispatch
end
def segments
- @path.names
+ path.names
end
def required_keys
- path.required_names.map { |x| x.to_sym } + required_defaults.keys
+ required_parts + required_defaults.keys
end
def score(constraints)
@@ -83,12 +79,38 @@ module ActionDispatch
@required_parts ||= path.required_names.map { |n| n.to_sym }
end
+ def required_default?(key)
+ (constraints[:required_defaults] || []).include?(key)
+ end
+
def required_defaults
- @required_defaults ||= begin
- matches = parts
- @defaults.dup.delete_if { |k,_| matches.include?(k) }
+ @required_defaults ||= @defaults.dup.delete_if do |k,_|
+ parts.include?(k) || !required_default?(k)
end
end
+
+ def matches?(request)
+ constraints.all? do |method, value|
+ next true unless request.respond_to?(method)
+
+ case value
+ when Regexp, String
+ value === request.send(method).to_s
+ when Array
+ value.include?(request.send(method))
+ else
+ value === request.send(method)
+ end
+ end
+ end
+
+ def ip
+ constraints[:ip] || //
+ end
+
+ def verb
+ constraints[:request_method] || //
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb
index 1fc45a2109..31868b1814 100644
--- a/actionpack/lib/action_dispatch/journey/router.rb
+++ b/actionpack/lib/action_dispatch/journey/router.rb
@@ -131,11 +131,7 @@ module ActionDispatch
}
routes.concat get_routes_as_head(routes)
- routes.sort_by!(&:precedence).select! { |r|
- r.constraints.all? { |k, v| v === req.send(k) } &&
- r.verb === req.request_method
- }
- routes.reject! { |r| req.ip && !(r.ip === req.ip) }
+ routes.sort_by!(&:precedence).select! { |r| r.matches?(req) }
routes.map! { |r|
match_data = r.path.match(req.path_info)
diff --git a/actionpack/lib/action_dispatch/journey/routes.rb b/actionpack/lib/action_dispatch/journey/routes.rb
index 32829a1f20..a99d6d0d6a 100644
--- a/actionpack/lib/action_dispatch/journey/routes.rb
+++ b/actionpack/lib/action_dispatch/journey/routes.rb
@@ -16,12 +16,12 @@ module ActionDispatch
end
def length
- @routes.length
+ routes.length
end
alias :size :length
def last
- @routes.last
+ routes.last
end
def each(&block)
@@ -33,24 +33,23 @@ module ActionDispatch
end
def partitioned_routes
- @partitioned_routes ||= routes.partition { |r|
- r.path.anchored && r.ast.grep(Nodes::Symbol).all? { |n| n.default_regexp? }
- }
+ @partitioned_routes ||= routes.partition do |r|
+ r.path.anchored && r.ast.grep(Nodes::Symbol).all?(&:default_regexp?)
+ end
end
def ast
- return @ast if @ast
- return if partitioned_routes.first.empty?
-
- asts = partitioned_routes.first.map { |r| r.ast }
- @ast = Nodes::Or.new(asts)
+ @ast ||= begin
+ asts = partitioned_routes.first.map(&:ast)
+ Nodes::Or.new(asts) unless asts.empty?
+ end
end
def simulator
- return @simulator if @simulator
-
- gtg = GTG::Builder.new(ast).transition_table
- @simulator = GTG::Simulator.new(gtg)
+ @simulator ||= begin
+ gtg = GTG::Builder.new(ast).transition_table
+ GTG::Simulator.new(gtg)
+ end
end
# Add a route to the routing table.
diff --git a/actionpack/lib/action_dispatch/middleware/best_standards_support.rb b/actionpack/lib/action_dispatch/middleware/best_standards_support.rb
index d338996240..94efeb79fa 100644
--- a/actionpack/lib/action_dispatch/middleware/best_standards_support.rb
+++ b/actionpack/lib/action_dispatch/middleware/best_standards_support.rb
@@ -17,7 +17,9 @@ module ActionDispatch
status, headers, body = @app.call(env)
if headers["X-UA-Compatible"] && @header
- headers["X-UA-Compatible"] << "," << @header.to_s
+ unless headers["X-UA-Compatible"][@header]
+ headers["X-UA-Compatible"] << "," << @header.to_s
+ end
else
headers["X-UA-Compatible"] = @header
end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index 121a11c8e1..6ecbb03784 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -15,7 +15,7 @@ module ActionDispatch
# being written will be sent out with the response. Reading a cookie does not get
# the cookie object itself back, just the value it holds.
#
- # Examples for writing:
+ # Examples of writing:
#
# # Sets a simple session cookie.
# # This cookie will be deleted when the user's browser is closed.
@@ -38,7 +38,7 @@ module ActionDispatch
# # You can also chain these methods:
# cookies.permanent.signed[:login] = "XJ-122"
#
- # Examples for reading:
+ # Examples of reading:
#
# cookies[:user_name] # => "david"
# cookies.size # => 2
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
index ac8f6af7fe..64230ff1ae 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -2,7 +2,6 @@ require 'action_dispatch/http/request'
require 'action_dispatch/middleware/exception_wrapper'
require 'action_dispatch/routing/inspector'
-
module ActionDispatch
# This middleware is responsible for logging exceptions and
# showing a debugging page in case the request is local.
@@ -15,18 +14,17 @@ module ActionDispatch
end
def call(env)
- begin
- response = (_, headers, body = @app.call(env))
-
- if headers['X-Cascade'] == 'pass'
- body.close if body.respond_to?(:close)
- raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
- end
- rescue Exception => exception
- raise exception if env['action_dispatch.show_exceptions'] == false
+ _, headers, body = response = @app.call(env)
+
+ if headers['X-Cascade'] == 'pass'
+ body.close if body.respond_to?(:close)
+ raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
end
- exception ? render_exception(env, exception) : response
+ response
+ rescue Exception => exception
+ raise exception if env['action_dispatch.show_exceptions'] == false
+ render_exception(env, exception)
end
private
@@ -42,12 +40,11 @@ module ActionDispatch
:application_trace => wrapper.application_trace,
:framework_trace => wrapper.framework_trace,
:full_trace => wrapper.full_trace,
- :routes => formatted_routes(exception),
+ :routes_inspector => routes_inspector(exception),
:source_extract => wrapper.source_extract,
:line_number => wrapper.line_number,
:file => wrapper.file
)
-
file = "rescues/#{wrapper.rescue_template}"
body = template.render(:template => file, :layout => 'rescues/layout')
render(wrapper.status_code, body)
@@ -85,11 +82,9 @@ module ActionDispatch
@stderr_logger ||= ActiveSupport::Logger.new($stderr)
end
- def formatted_routes(exception)
- return false unless @routes_app.respond_to?(:routes)
- if exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error)
- inspector = ActionDispatch::Routing::RoutesInspector.new
- inspector.collect_routes(@routes_app.routes.routes)
+ def routes_inspector(exception)
+ if @routes_app.respond_to?(:routes) && (exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error))
+ ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
index 58adf22862..7489ce8028 100644
--- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
+++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
@@ -12,7 +12,8 @@ module ActionDispatch
'ActionController::NotImplemented' => :not_implemented,
'ActionController::UnknownFormat' => :not_acceptable,
'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
- 'ActionController::BadRequest' => :bad_request
+ 'ActionController::BadRequest' => :bad_request,
+ 'ActionController::ParameterMissing' => :bad_request
)
cattr_accessor :rescue_templates
@@ -96,7 +97,7 @@ module ActionDispatch
if File.exists?(full_path)
File.open(full_path, "r") do |file|
start = [line - 3, 0].max
- lines = file.lines.drop(start).take(6)
+ lines = file.each_line.drop(start).take(6)
Hash[*(start+1..(lines.count+start)).zip(lines).flatten]
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index f24e9b8e18..7e56feb90a 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -59,12 +59,12 @@ module ActionDispatch
@flash[k]
end
- # Convenience accessor for flash.now[:alert]=
+ # Convenience accessor for <tt>flash.now[:alert]=</tt>.
def alert=(message)
self[:alert] = message
end
- # Convenience accessor for flash.now[:notice]=
+ # Convenience accessor for <tt>flash.now[:notice]=</tt>.
def notice=(message)
self[:notice] = message
end
@@ -82,7 +82,7 @@ module ActionDispatch
else
new
end
-
+
flash.tap(&:sweep)
end
@@ -169,6 +169,14 @@ module ActionDispatch
# vanish when the current action is done.
#
# Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
+ #
+ # Also, brings two convenience accessors:
+ #
+ # flash.now.alert = "Beware now!"
+ # # Equivalent to flash.now[:alert] = "Beware now!"
+ #
+ # flash.now.notice = "Good luck now!"
+ # # Equivalent to flash.now[:notice] = "Good luck now!"
def now
@now ||= FlashNow.new(self)
end
@@ -199,22 +207,22 @@ module ActionDispatch
@discard.replace @flashes.keys
end
- # Convenience accessor for flash[:alert]
+ # Convenience accessor for <tt>flash[:alert]</tt>.
def alert
self[:alert]
end
- # Convenience accessor for flash[:alert]=
+ # Convenience accessor for <tt>flash[:alert]=</tt>.
def alert=(message)
self[:alert] = message
end
- # Convenience accessor for flash[:notice]
+ # Convenience accessor for <tt>flash[:notice]</tt>.
def notice
self[:notice]
end
- # Convenience accessor for flash[:notice]=
+ # Convenience accessor for <tt>flash[:notice]=</tt>.
def notice=(message)
self[:notice] = message
end
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index 2c98ca03a8..0898ad82dd 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -47,14 +47,12 @@ module ActionDispatch
when Proc
strategy.call(request.raw_post)
when :xml_simple, :xml_node
- data = Hash.from_xml(request.raw_post) || {}
+ data = request.deep_munge(Hash.from_xml(request.body.read) || {})
data.with_indifferent_access
- when :yaml
- YAML.load(request.raw_post)
when :json
- data = ActiveSupport::JSON.decode(request.raw_post)
+ data = ActiveSupport::JSON.decode(request.body)
data = {:_json => data} unless data.is_a?(Hash)
- data.with_indifferent_access
+ request.deep_munge(data).with_indifferent_access
else
false
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index ce5f89ee5b..1e6ed624b0 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -21,15 +21,12 @@ module ActionDispatch
#
# Session options:
#
- # * <tt>:secret</tt>: An application-wide key string or block returning a
- # string called per generated digest. The block is called with the
- # CGI::Session instance as an argument. It's important that the secret
- # is not vulnerable to a dictionary attack. Therefore, you should choose
- # a secret consisting of random numbers and letters and more than 30
- # characters.
+ # * <tt>:secret</tt>: An application-wide key string. It's important that
+ # the secret is not vulnerable to a dictionary attack. Therefore, you
+ # should choose a secret consisting of random numbers and letters and
+ # more than 30 characters.
#
# secret: '449fe2e7daee471bffae2fd8dc02313d'
- # secret: Proc.new { User.current_user.secret_key }
#
# * <tt>:digest</tt>: The message digest algorithm used to verify session
# integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
@@ -39,21 +36,38 @@ module ActionDispatch
# "rake secret" and set the key in config/initializers/secret_token.rb.
#
# Note that changing digest or secret invalidates all existing sessions!
- class CookieStore < Rack::Session::Cookie
+ class CookieStore < Rack::Session::Abstract::ID
include Compatibility
include StaleSessionCheck
include SessionObject
- # Override rack's method
+ def initialize(app, options={})
+ super(app, options.merge!(:cookie_only => true))
+ end
+
def destroy_session(env, session_id, options)
- new_sid = super
+ new_sid = generate_sid unless options[:drop]
# Reset hash and Assign the new session id
env["action_dispatch.request.unsigned_session_cookie"] = new_sid ? { "session_id" => new_sid } : {}
new_sid
end
+ def load_session(env)
+ stale_session_check! do
+ data = unpacked_cookie_data(env)
+ data = persistent_session_id!(data)
+ [data["session_id"], data]
+ end
+ end
+
private
+ def extract_session_id(env)
+ stale_session_check! do
+ unpacked_cookie_data(env)["session_id"]
+ end
+ end
+
def unpacked_cookie_data(env)
env["action_dispatch.request.unsigned_session_cookie"] ||= begin
stale_session_check! do
@@ -65,6 +79,12 @@ module ActionDispatch
end
end
+ def persistent_session_id!(data, sid=nil)
+ data ||= {}
+ data["session_id"] ||= sid || generate_sid
+ data
+ end
+
def set_session(env, sid, session_data, options)
session_data["session_id"] = sid
session_data
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 2b37a8d026..fcc5bc12c4 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -16,9 +16,9 @@ module ActionDispatch
# catches the exceptions and returns a FAILSAFE_RESPONSE.
class ShowExceptions
FAILSAFE_RESPONSE = [500, { 'Content-Type' => 'text/plain' },
- ["500 Internal Server Error\n" <<
- "If you are the administrator of this website, then please read this web " <<
- "application's log file and/or the web server's log file to find out what " <<
+ ["500 Internal Server Error\n" \
+ "If you are the administrator of this website, then please read this web " \
+ "application's log file and/or the web server's log file to find out what " \
"went wrong."]]
def initialize(app, exceptions_app)
@@ -27,13 +27,10 @@ module ActionDispatch
end
def call(env)
- begin
- response = @app.call(env)
- rescue Exception => exception
- raise exception if env['action_dispatch.show_exceptions'] == false
- end
-
- response || render_exception(env, exception)
+ @app.call(env)
+ rescue Exception => exception
+ raise exception if env['action_dispatch.show_exceptions'] == false
+ render_exception(env, exception)
end
private
diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb
index 9098f4e170..9e03cbf2b7 100644
--- a/actionpack/lib/action_dispatch/middleware/ssl.rb
+++ b/actionpack/lib/action_dispatch/middleware/ssl.rb
@@ -45,7 +45,7 @@ module ActionDispatch
# http://tools.ietf.org/html/draft-hodges-strict-transport-sec-02
def hsts_headers
if @hsts
- value = "max-age=#{@hsts[:expires]}"
+ value = "max-age=#{@hsts[:expires].to_i}"
value += "; includeSubDomains" if @hsts[:subdomains]
{ 'Strict-Transport-Security' => value }
else
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index e3b15b43b9..c6a7d9c415 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -6,7 +6,8 @@ module ActionDispatch
def initialize(root, cache_control)
@root = root.chomp('/')
@compiled_root = /^#{Regexp.escape(root)}/
- @file_server = ::Rack::File.new(@root, cache_control)
+ headers = cache_control && { 'Cache-Control' => cache_control }
+ @file_server = ::Rack::File.new(@root, headers)
end
def match?(path)
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
index fbb47f60a2..ab24118f3e 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
@@ -1,8 +1,8 @@
<% unless @exception.blamed_files.blank? %>
<% if (hide = @exception.blamed_files.length > 8) %>
- <a href="#" onclick="s = document.getElementById('blame_trace').style; s.display = s.display == 'none' ? 'block' : 'none'; return false;">Toggle blamed files</a>
+ <a href="#" onclick="toggleTrace()">Toggle blamed files</a>
<% end %>
- <pre id="blame_trace" <%='style="display:none"' if hide %>><code><%=h @exception.describe_blame %></code></pre>
+ <pre id="blame_trace" <%='style="display:none"' if hide %>><code><%= @exception.describe_blame %></code></pre>
<% end %>
<%
@@ -18,17 +18,17 @@
%>
<h2 style="margin-top: 30px">Request</h2>
-<p><b>Parameters</b>:</p> <pre><%=h request_dump %></pre>
+<p><b>Parameters</b>:</p> <pre><%= request_dump %></pre>
<div class="details">
- <div class="summary"><a href="#" onclick="s = document.getElementById('session_dump').style; s.display = s.display == 'none' ? 'block' : 'none'; return false;">Toggle session dump</a></div>
+ <div class="summary"><a href="#" onclick="toggleSessionDump()">Toggle session dump</a></div>
<div id="session_dump" style="display:none"><pre><%= debug_hash @request.session %></pre></div>
</div>
<div class="details">
- <div class="summary"><a href="#" onclick="s = document.getElementById('env_dump').style; s.display = s.display == 'none' ? 'block' : 'none'; return false;">Toggle env dump</a></div>
+ <div class="summary"><a href="#" onclick="toggleEnvDump()">Toggle env dump</a></div>
<div id="env_dump" style="display:none"><pre><%= debug_hash @request.env.slice(*@request.class::ENV_METHODS) %></pre></div>
</div>
<h2 style="margin-top: 30px">Response</h2>
-<p><b>Headers</b>:</p> <pre><%=h defined?(@response) ? @response.headers.inspect.gsub(',', ",\n") : 'None' %></pre>
+<p><b>Headers</b>:</p> <pre><%= defined?(@response) ? @response.headers.inspect.gsub(',', ",\n") : 'None' %></pre>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
index 8771b5fd6d..9d947aea40 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
@@ -12,15 +12,15 @@
<div id="traces">
<% names.each do |name| %>
<%
- show = "document.getElementById('#{name.gsub(/\s/, '-')}').style.display='block';"
- hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub(/\s/, '-')}').style.display='none';"}
+ show = "show('#{name.gsub(/\s/, '-')}');"
+ hide = (names - [name]).collect {|hide_name| "hide('#{hide_name.gsub(/\s/, '-')}');"}
%>
<a href="#" onclick="<%= hide.join %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
<% end %>
<% traces.each do |name, trace| %>
<div id="<%= name.gsub(/\s/, '-') %>" style="display: <%= (name == "Application Trace") ? 'block' : 'none' %>;">
- <pre><code><%=h trace.join "\n" %></code></pre>
+ <pre><code><%= trace.join "\n" %></code></pre>
</div>
<% end %>
</div>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
index 1c6b5010a3..57a2940802 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
@@ -1,14 +1,14 @@
<header>
<h1>
- <%=h @exception.class.to_s %>
+ <%= @exception.class.to_s %>
<% if @request.parameters['controller'] %>
- in <%=h @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%=h @request.parameters['action'] %><% end %>
+ in <%= @request.parameters['controller'].camelize %>Controller<% if @request.parameters['action'] %>#<%= @request.parameters['action'] %><% end %>
<% end %>
</h1>
</header>
<div id="container">
- <h2><%=h @exception.message %></h2>
+ <h2><%= @exception.message %></h2>
<%= render template: "rescues/_source" %>
<%= render template: "rescues/_trace" %>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
index ff33430532..9878c2747e 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb
@@ -113,7 +113,31 @@
a { color: #980905; }
a:visited { color: #666; }
a:hover { color: #C52F24; }
+
+ <%= yield :style %>
</style>
+
+ <script>
+ var toggle = function(id) {
+ var s = document.getElementById(id).style;
+ s.display = s.display == 'none' ? 'block' : 'none';
+ }
+ var show = function(id) {
+ document.getElementById(id).style.display = 'block';
+ }
+ var hide = function(id) {
+ document.getElementById(id).style.display = 'none';
+ }
+ var toggleTrace = function() {
+ toggle('blame_trace');
+ }
+ var toggleSessionDump = function() {
+ toggle('session_dump');
+ }
+ var toggleEnvDump = function() {
+ toggle('env_dump');
+ }
+ </script>
</head>
<body>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_template.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_template.erb
index c5917b9acb..ca14215946 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_template.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/missing_template.erb
@@ -3,5 +3,5 @@
</header>
<div id="container">
- <h2><%=h @exception.message %></h2>
+ <h2><%= @exception.message %></h2>
</div>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
index ca85e6d048..61690d3e50 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/routing_error.erb
@@ -2,27 +2,29 @@
<h1>Routing Error</h1>
</header>
<div id="container">
- <h2><%=h @exception.message %></h2>
+ <h2><%= @exception.message %></h2>
<% unless @exception.failures.empty? %>
<p>
<h2>Failure reasons:</h2>
<ol>
<% @exception.failures.each do |route, reason| %>
- <li><code><%=h route.inspect.gsub('\\', '') %></code> failed because <%=h reason.downcase %></li>
+ <li><code><%= route.inspect.gsub('\\', '') %></code> failed because <%= reason.downcase %></li>
<% end %>
</ol>
</p>
<% end %>
+
<%= render template: "rescues/_trace" %>
- <h2>
- Routes
- </h2>
+ <% if @routes_inspector %>
+ <h2>
+ Routes
+ </h2>
- <p>
- Routes match in priority from top to bottom
- </p>
+ <p>
+ Routes match in priority from top to bottom
+ </p>
-<%= render layout: "routes/route_wrapper" do %>
- <%= render partial: "routes/route", collection: @routes %>
-<% end %>
+ <%= @routes_inspector.format(ActionDispatch::Routing::HtmlTableFormatter.new(self)) %>
+ <% end %>
+</div>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb
index 9f3816bf40..63216ef7c5 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/template_error.erb
@@ -1,20 +1,20 @@
<% @source_extract = @exception.source_extract(0, :html) %>
<header>
<h1>
- <%=h @exception.original_exception.class.to_s %> in
- <%=h @request.parameters["controller"].capitalize if @request.parameters["controller"]%>#<%=h @request.parameters["action"] %>
+ <%= @exception.original_exception.class.to_s %> in
+ <%= @request.parameters["controller"].capitalize if @request.parameters["controller"]%>#<%= @request.parameters["action"] %>
</h1>
</header>
<div id="container">
<p>
- Showing <i><%=h @exception.file_name %></i> where line <b>#<%=h @exception.line_number %></b> raised:
+ Showing <i><%= @exception.file_name %></i> where line <b>#<%= @exception.line_number %></b> raised:
</p>
- <pre><code><%=h @exception.message %></code></pre>
+ <pre><code><%= @exception.message %></code></pre>
<div class="source">
<div class="info">
- <p>Extracted source (around line <strong>#<%=h @exception.line_number %></strong>):</p>
+ <p>Extracted source (around line <strong>#<%= @exception.line_number %></strong>):</p>
</div>
<div class="data">
<table cellpadding="0" cellspacing="0" class="lines">
@@ -36,7 +36,7 @@
</div>
</div>
- <p><%=h @exception.sub_template_message %></p>
+ <p><%= @exception.sub_template_message %></p>
<%= render template: "rescues/_trace" %>
<%= render template: "rescues/_request_and_response" %>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb
index 0ed1b188c1..c1fbf67eed 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb
@@ -2,5 +2,5 @@
<h1>Unknown action</h1>
</header>
<div id="container">
- <h2><%=h @exception.message %></h2>
+ <h2><%= @exception.message %></h2>
</div>
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/_route_wrapper.html.erb b/actionpack/lib/action_dispatch/middleware/templates/routes/_route_wrapper.html.erb
deleted file mode 100644
index dc17cb77ef..0000000000
--- a/actionpack/lib/action_dispatch/middleware/templates/routes/_route_wrapper.html.erb
+++ /dev/null
@@ -1,56 +0,0 @@
-<style type='text/css'>
- #route_table td {padding: 0 30px;}
- #route_table {margin: 0 auto 0;}
-</style>
-
-<table id='route_table' class='route_table'>
- <thead>
- <tr>
- <th>Helper<br />
- <%= 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)" %>
- </th>
- <th>HTTP Verb</th>
- <th>Path</th>
- <th>Controller#Action</th>
- </tr>
- </thead>
- <tbody>
- <%= yield %>
- </tbody>
-</table>
-
-<script type='text/javascript'>
- function each(elems, func) {
- if (!elems instanceof Array) { elems = [elems]; }
- for (var i = elems.length; i--; ) {
- func(elems[i]);
- }
- }
-
- function setValOn(elems, val) {
- each(elems, function(elem) {
- elem.innerHTML = val;
- });
- }
-
- function onClick(elems, func) {
- each(elems, function(elem) {
- elem.onclick = func;
- });
- }
-
- // Enables functionality to toggle between `_path` and `_url` helper suffixes
- 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');
- setValOn(helperElems, helperTxt);
- });
- }
-
- setupRouteToggleHelperLinks();
-</script>
diff --git a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
new file mode 100644
index 0000000000..95461fa693
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb
@@ -0,0 +1,144 @@
+<% content_for :style do %>
+ #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</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)" %>
+ </th>
+ <th><%# HTTP Verb %>
+ </th>
+ <th><%# Path %>
+ <%= search_field(:path, nil, id: 'path_search', placeholder: "Path Match") %>
+ </th>
+ <th><%# Controller#action %>
+ </th>
+ </tr>
+ </thead>
+ <tbody class='matched_paths' id='matched_paths'>
+ </tbody>
+ <tbody>
+ <%= yield %>
+ </tbody>
+</table>
+
+<script type='text/javascript'>
+ function each(elems, func) {
+ if (!elems instanceof Array) { elems = [elems]; }
+ for (var i = 0, len = elems.length; i < len; i++) {
+ func(elems[i]);
+ }
+ }
+
+ function setValOn(elems, val) {
+ each(elems, function(elem) {
+ elem.innerHTML = val;
+ });
+ }
+
+ function onClick(elems, func) {
+ each(elems, function(elem) {
+ elem.onclick = func;
+ });
+ }
+
+ // Enables functionality to toggle between `_path` and `_url` helper suffixes
+ function setupRouteToggleHelperLinks() {
+ var toggleLinks = document.querySelectorAll('#route_table [data-route-helper]');
+ onClick(toggleLinks, function(){
+ 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/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
index 73b2b4ac1d..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
@@ -61,32 +78,51 @@ module ActionDispatch
##
# This class is just used for displaying route information when someone
- # executes `rake routes`. People should not use this class.
+ # executes `rake routes` or looks at the RoutingError page.
+ # People should not use this class.
class RoutesInspector # :nodoc:
- def initialize
- @engines = Hash.new
+ def initialize(routes)
+ @engines = {}
+ @routes = routes
end
- def format(all_routes, filter = nil)
- if filter
- all_routes = all_routes.select{ |route| route.defaults[:controller] == filter }
+ def format(formatter, filter = nil)
+ routes_to_display = filter_routes(filter)
+
+ routes = collect_routes(routes_to_display)
+ formatter.section routes
+
+ @engines.each do |name, engine_routes|
+ formatter.section_title "Routes for #{name}"
+ formatter.section engine_routes
end
- routes = collect_routes(all_routes)
+ formatter.result
+ end
+
+ private
- formatted_routes(routes) +
- formatted_routes_for_engines
+ def filter_routes(filter)
+ if filter
+ @routes.select { |route| route.defaults[:controller] == filter }
+ else
+ @routes
+ end
end
def collect_routes(routes)
- routes = routes.collect do |route|
+ routes.collect do |route|
RouteWrapper.new(route)
end.reject do |route|
route.internal?
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
@@ -100,21 +136,55 @@ module ActionDispatch
@engines[name] = collect_routes(routes.routes)
end
end
+ end
- def formatted_routes_for_engines
- @engines.map do |name, routes|
- ["\nRoutes for #{name}:"] + formatted_routes(routes)
- end.flatten
+ class ConsoleFormatter
+ def initialize
+ @buffer = []
end
- def formatted_routes(routes)
- name_width = routes.map{ |r| r[:name].length }.max
- verb_width = routes.map{ |r| r[:verb].length }.max
- path_width = routes.map{ |r| r[:path].length }.max
+ def result
+ @buffer.join("\n")
+ end
- routes.map do |r|
- "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
+ def section_title(title)
+ @buffer << "\n#{title}:"
+ end
+
+ def section(routes)
+ @buffer << draw_section(routes)
+ end
+
+ private
+ def draw_section(routes)
+ name_width = routes.map { |r| r[:name].length }.max
+ verb_width = routes.map { |r| r[:verb].length }.max
+ path_width = routes.map { |r| r[:path].length }.max
+
+ routes.map do |r|
+ "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
+ end
end
+ end
+
+ class HtmlTableFormatter
+ def initialize(view)
+ @view = view
+ @buffer = []
+ end
+
+ 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
+
+ def result
+ @view.raw @view.render(layout: "routes/table") {
+ @view.raw @buffer.join("\n")
+ }
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index a21383e091..3a86432622 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -8,6 +8,8 @@ require 'action_dispatch/routing/redirection'
module ActionDispatch
module Routing
class Mapper
+ URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port]
+
class Constraints #:nodoc:
def self.new(app, constraints, request = Rack::Request)
if constraints.any?
@@ -26,9 +28,9 @@ module ActionDispatch
def matches?(env)
req = @request.new(env)
- @constraints.none? do |constraint|
- (constraint.respond_to?(:matches?) && !constraint.matches?(req)) ||
- (constraint.respond_to?(:call) && !constraint.call(*constraint_args(constraint, req)))
+ @constraints.all? do |constraint|
+ (constraint.respond_to?(:matches?) && constraint.matches?(req)) ||
+ (constraint.respond_to?(:call) && constraint.call(*constraint_args(constraint, req)))
end
ensure
req.reset_parameters
@@ -45,37 +47,68 @@ module ActionDispatch
end
class Mapping #:nodoc:
- IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix]
+ IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix, :format]
ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
SHORTHAND_REGEX = %r{/[\w/]+$}
WILDCARD_PATH = %r{\*([^/\)]+)\)?$}
- def initialize(set, scope, path, options)
- @set, @scope = set, scope
- @segment_keys = nil
- @options = (@scope[:options] || {}).merge(options)
- @path = normalize_path(path)
- normalize_options!
+ attr_reader :scope, :path, :options, :requirements, :conditions, :defaults
- via_all = @options.delete(:via) if @options[:via] == :all
+ def initialize(set, scope, path, options)
+ @set, @scope, @path, @options = set, scope, path, options
+ @requirements, @conditions, @defaults = {}, {}, {}
- if !via_all && request_method_condition.empty?
- msg = "You should not use the `match` method in your router without specifying an HTTP method.\n" \
- "If you want to expose your action to GET, use `get` in the router:\n\n" \
- " Instead of: match \"controller#action\"\n" \
- " Do: get \"controller#action\""
- raise msg
- end
+ normalize_path!
+ normalize_options!
+ normalize_requirements!
+ normalize_conditions!
+ normalize_defaults!
end
def to_route
- [ app, conditions, requirements, defaults, @options[:as], @options[:anchor] ]
+ [ app, conditions, requirements, defaults, options[:as], options[:anchor] ]
end
private
+ def normalize_path!
+ raise ArgumentError, "path is required" if @path.blank?
+ @path = Mapper.normalize_path(@path)
+
+ if required_format?
+ @path = "#{@path}.:format"
+ elsif optional_format?
+ @path = "#{@path}(.:format)"
+ end
+ end
+
+ def required_format?
+ options[:format] == true
+ end
+
+ def optional_format?
+ options[:format] != false && !path.include?(':format') && !path.end_with?('/')
+ end
+
def normalize_options!
- path_without_format = @path.sub(/\(\.:format\)$/, '')
+ @options.reverse_merge!(scope[:options]) if scope[:options]
+ path_without_format = path.sub(/\(\.:format\)$/, '')
+
+ # Add a constraint for wildcard route to make it non-greedy and match the
+ # optional format part of the route by default
+ if path_without_format.match(WILDCARD_PATH) && @options[:format] != false
+ @options[$1.to_sym] ||= /.+?/
+ end
+
+ if path_without_format.match(':controller')
+ raise ArgumentError, ":controller segment is not allowed within a namespace block" if scope[:module]
+
+ # Add a default constraint for :controller path segments that matches namespaced
+ # controllers with default routes like :controller/:action/:id(.:format), e.g:
+ # GET /admin/products/show/1
+ # => { controller: 'admin/products', action: 'show', id: '1' }
+ @options[:controller] ||= /.+?/
+ end
if using_match_shorthand?(path_without_format, @options)
to_shorthand = @options[:to].blank?
@@ -83,85 +116,101 @@ module ActionDispatch
end
@options.merge!(default_controller_and_action(to_shorthand))
+ end
- requirements.each do |name, requirement|
- # segment_keys.include?(k.to_s) || k == :controller
- next unless Regexp === requirement && !constraints[name]
+ # match "account/overview"
+ def using_match_shorthand?(path, options)
+ path && (options[:to] || options[:action]).nil? && path =~ SHORTHAND_REGEX
+ end
+
+ def normalize_format!
+ if options[:format] == true
+ options[:format] = /.+/
+ elsif options[:format] == false
+ options.delete(:format)
+ end
+ end
+
+ def normalize_requirements!
+ constraints.each do |key, requirement|
+ next unless segment_keys.include?(key) || key == :controller
if requirement.source =~ ANCHOR_CHARACTERS_REGEX
raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
end
+
if requirement.multiline?
- raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
+ raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
end
+
+ @requirements[key] = requirement
end
- if @options[:constraints].is_a?(Hash)
- (@options[:defaults] ||= {}).reverse_merge!(defaults_from_constraints(@options[:constraints]))
+ if options[:format] == true
+ @requirements[:format] = /.+/
+ elsif Regexp === options[:format]
+ @requirements[:format] = options[:format]
+ elsif String === options[:format]
+ @requirements[:format] = Regexp.compile(options[:format])
end
end
- # match "account/overview"
- def using_match_shorthand?(path, options)
- path && (options[:to] || options[:action]).nil? && path =~ SHORTHAND_REGEX
- end
+ def normalize_defaults!
+ @defaults.merge!(scope[:defaults]) if scope[:defaults]
+ @defaults.merge!(options[:defaults]) if options[:defaults]
- def normalize_path(path)
- raise ArgumentError, "path is required" if path.blank?
- path = Mapper.normalize_path(path)
+ options.each do |key, default|
+ next if Regexp === default || IGNORE_OPTIONS.include?(key)
+ @defaults[key] = default
+ end
- if path.match(':controller')
- raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module]
+ if options[:constraints].is_a?(Hash)
+ options[:constraints].each do |key, default|
+ next unless URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
+ @defaults[key] ||= default
+ end
+ end
- # Add a default constraint for :controller path segments that matches namespaced
- # controllers with default routes like :controller/:action/:id(.:format), e.g:
- # GET /admin/products/show/1
- # => { controller: 'admin/products', action: 'show', id: '1' }
- @options[:controller] ||= /.+?/
+ if Regexp === options[:format]
+ @defaults[:format] = nil
+ elsif String === options[:format]
+ @defaults[:format] = options[:format]
end
+ end
- # Add a constraint for wildcard route to make it non-greedy and match the
- # optional format part of the route by default
- if path.match(WILDCARD_PATH) && @options[:format] != false
- @options[$1.to_sym] ||= /.+?/
+ def normalize_conditions!
+ @conditions.merge!(:path_info => path)
+
+ constraints.each do |key, condition|
+ next if segment_keys.include?(key) || key == :controller
+ @conditions[key] = condition
end
- if @options[:format] == false
- @options.delete(:format)
- path
- elsif path.include?(":format") || path.end_with?('/')
- path
- elsif @options[:format] == true
- "#{path}.:format"
- else
- "#{path}(.:format)"
+ @conditions[:required_defaults] = []
+ options.each do |key, required_default|
+ next if segment_keys.include?(key) || IGNORE_OPTIONS.include?(key)
+ next if Regexp === required_default
+ @conditions[:required_defaults] << key
end
- end
- def app
- Constraints.new(
- to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults),
- blocks,
- @set.request_class
- )
- end
+ via_all = options.delete(:via) if options[:via] == :all
- def conditions
- { :path_info => @path }.merge!(constraints).merge!(request_method_condition)
- end
+ if !via_all && options[:via].blank?
+ msg = "You should not use the `match` method in your router without specifying an HTTP method.\n" \
+ "If you want to expose your action to GET, use `get` in the router:\n\n" \
+ " Instead of: match \"controller#action\"\n" \
+ " Do: get \"controller#action\""
+ raise msg
+ end
- def requirements
- @requirements ||= (@options[:constraints].is_a?(Hash) ? @options[:constraints] : {}).tap do |requirements|
- requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints]
- @options.each { |k, v| requirements[k] ||= v if v.is_a?(Regexp) }
+ if via = options[:via]
+ list = Array(via).map { |m| m.to_s.dasherize.upcase }
+ @conditions.merge!(:request_method => list)
end
end
- def defaults
- @defaults ||= (@options[:defaults] || {}).tap do |defaults|
- defaults.reverse_merge!(@scope[:defaults]) if @scope[:defaults]
- @options.each { |k, v| defaults[k] = v unless v.is_a?(Regexp) || IGNORE_OPTIONS.include?(k.to_sym) }
- end
+ def app
+ Constraints.new(endpoint, blocks, @set.request_class)
end
def default_controller_and_action(to_shorthand=nil)
@@ -188,11 +237,11 @@ module ActionDispatch
controller = controller.to_s unless controller.is_a?(Regexp)
action = action.to_s unless action.is_a?(Regexp)
- if controller.blank? && segment_keys.exclude?("controller")
+ if controller.blank? && segment_keys.exclude?(:controller)
raise ArgumentError, "missing :controller"
end
- if action.blank? && segment_keys.exclude?("action")
+ if action.blank? && segment_keys.exclude?(:action)
raise ArgumentError, "missing :action"
end
@@ -204,50 +253,55 @@ module ActionDispatch
end
def blocks
- constraints = @options[:constraints]
- if constraints.present? && !constraints.is_a?(Hash)
- [constraints]
+ if options[:constraints].present? && !options[:constraints].is_a?(Hash)
+ [options[:constraints]]
else
- @scope[:blocks] || []
+ scope[:blocks] || []
end
end
def constraints
- @constraints ||= requirements.reject { |k, v| segment_keys.include?(k.to_s) || k == :controller }
- end
+ @constraints ||= {}.tap do |constraints|
+ constraints.merge!(scope[:constraints]) if scope[:constraints]
- def request_method_condition
- if via = @options[:via]
- list = Array(via).map { |m| m.to_s.dasherize.upcase }
- { :request_method => list }
- else
- { }
+ options.except(*IGNORE_OPTIONS).each do |key, option|
+ constraints[key] = option if Regexp === option
+ end
+
+ constraints.merge!(options[:constraints]) if options[:constraints].is_a?(Hash)
end
end
def segment_keys
- return @segment_keys if @segment_keys
+ @segment_keys ||= path_pattern.names.map{ |s| s.to_sym }
+ end
+
+ def path_pattern
+ Journey::Path::Pattern.new(strexp)
+ end
- @segment_keys = Journey::Path::Pattern.new(
- Journey::Router::Strexp.compile(@path, requirements, SEPARATORS)
- ).names
+ def strexp
+ Journey::Router::Strexp.compile(path, requirements, SEPARATORS)
+ end
+
+ def endpoint
+ to.respond_to?(:call) ? to : dispatcher
+ end
+
+ def dispatcher
+ Routing::RouteSet::Dispatcher.new(:defaults => defaults)
end
def to
- @options[:to]
+ options[:to]
end
def default_controller
- @options[:controller] || @scope[:controller]
+ options[:controller] || scope[:controller]
end
def default_action
- @options[:action] || @scope[:action]
- end
-
- def defaults_from_constraints(constraints)
- url_keys = [:protocol, :subdomain, :domain, :host, :port]
- constraints.select { |k, v| url_keys.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum)) }
+ options[:action] || scope[:action]
end
end
@@ -641,7 +695,11 @@ module ActionDispatch
options[:constraints] ||= {}
if options[:constraints].is_a?(Hash)
- (options[:defaults] ||= {}).reverse_merge!(defaults_from_constraints(options[:constraints]))
+ defaults = options[:constraints].select do
+ |k, v| URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum))
+ end
+
+ (options[:defaults] ||= {}).reverse_merge!(defaults)
else
block, options[:constraints] = options[:constraints], {}
end
@@ -846,11 +904,6 @@ module ActionDispatch
def override_keys(child) #:nodoc:
child.key?(:only) || child.key?(:except) ? [:only, :except] : []
end
-
- def defaults_from_constraints(constraints)
- url_keys = [:protocol, :subdomain, :domain, :host, :port]
- constraints.select { |k, v| url_keys.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum)) }
- end
end
# Resource routing allows you to quickly declare all of the common routes
@@ -1350,9 +1403,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 b1959e388c..c72310cca3 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -421,7 +421,7 @@ module ActionDispatch
end
conditions.keep_if do |k, _|
- k == :action || k == :controller ||
+ k == :action || k == :controller || k == :required_defaults ||
@request_class.public_method_defined?(k) || path_values.include?(k)
end
end
@@ -527,12 +527,10 @@ module ActionDispatch
recall[:action] = options.delete(:action) if options[:action] == 'index'
end
- # Generates a path from routes, returns [path, params]
- # if no path is returned the formatter will raise Journey::Router::RoutingError
+ # Generates a path from routes, returns [path, params].
+ # If no route is generated the formatter will raise ActionController::UrlGenerationError
def generate
@set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE)
- rescue Journey::Router::RoutingError => e
- raise ActionController::UrlGenerationError, "No route matches #{options.inspect} #{e.message}"
end
def different_controller?
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index 79dff7d121..9210bffd1d 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -1,5 +1,6 @@
require 'uri'
require 'active_support/core_ext/hash/indifferent_access'
+require 'active_support/core_ext/string/access'
require 'action_controller/metal/exceptions'
module ActionDispatch
@@ -208,11 +209,9 @@ module ActionDispatch
end
def fail_on(exception_class)
- begin
- yield
- rescue exception_class => e
- raise MiniTest::Assertion, e.message
- end
+ yield
+ rescue exception_class => e
+ raise MiniTest::Assertion, e.message
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 1fc5933e98..ed4e88aab6 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -273,7 +273,7 @@ module ActionDispatch
if path =~ %r{://}
location = URI.parse(path)
https! URI::HTTPS === location if location.scheme
- host! location.host if location.host
+ host! "#{location.host}:#{location.port}" if location.host
path = location.query ? "#{location.path}?#{location.query}" : location.path
end
diff --git a/actionpack/lib/action_dispatch/testing/performance_test.rb b/actionpack/lib/action_dispatch/testing/performance_test.rb
deleted file mode 100644
index 13fe693c32..0000000000
--- a/actionpack/lib/action_dispatch/testing/performance_test.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require 'active_support/testing/performance'
-
-module ActionDispatch
- # An integration test that runs a code profiler on your test methods.
- # Profiling output for combinations of each test method, measurement, and
- # output format are written to your tmp/performance directory.
- class PerformanceTest < ActionDispatch::IntegrationTest
- include ActiveSupport::Testing::Performance
- end
-end
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 668515df59..08253de3f4 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -6,7 +6,7 @@ require 'action_view/log_subscriber'
module ActionView #:nodoc:
# = Action View Base
#
- # Action View templates can be written in several ways. If the template file has a <tt>.erb</tt> extension then it uses a mixture of ERb
+ # Action View templates can be written in several ways. If the template file has a <tt>.erb</tt> extension then it uses a mixture of ERB
# (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> extension then Jim Weirich's Builder::XmlMarkup library is used.
#
# == ERB
diff --git a/actionpack/lib/action_view/digestor.rb b/actionpack/lib/action_view/digestor.rb
index f3f6b425a8..4507861dcc 100644
--- a/actionpack/lib/action_view/digestor.rb
+++ b/actionpack/lib/action_view/digestor.rb
@@ -24,16 +24,17 @@ module ActionView
@@cache = ThreadSafe::Cache.new
def self.digest(name, format, finder, options = {})
- @@cache["#{name}.#{format}"] ||= begin
+ cache_key = [name, format] + Array.wrap(options[:dependencies])
+ @@cache[cache_key.join('.')] ||= begin
klass = options[:partial] || name.include?("/_") ? PartialDigestor : Digestor
- klass.new(name, format, finder).digest
+ klass.new(name, format, finder, options).digest
end
end
- attr_reader :name, :format, :finder
+ attr_reader :name, :format, :finder, :options
- def initialize(name, format, finder)
- @name, @format, @finder = name, format, finder
+ def initialize(name, format, finder, options={})
+ @name, @format, @finder, @options = name, format, finder, options
end
def digest
@@ -81,9 +82,11 @@ module ActionView
end
def dependency_digest
- dependencies.collect do |template_name|
+ template_digests = dependencies.collect do |template_name|
Digestor.digest(template_name, format, finder, partial: true)
- end.join("-")
+ end
+
+ (template_digests + injected_dependencies).join("-")
end
def render_dependencies
@@ -105,6 +108,10 @@ module ActionView
def explicit_dependencies
source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
end
+
+ def injected_dependencies
+ Array.wrap(options[:dependencies])
+ end
end
class PartialDigestor < Digestor # :nodoc:
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
index 269e78a021..8a78685ae1 100644
--- a/actionpack/lib/action_view/helpers.rb
+++ b/actionpack/lib/action_view/helpers.rb
@@ -1,3 +1,5 @@
+require 'active_support/benchmarkable'
+
module ActionView #:nodoc:
module Helpers #:nodoc:
extend ActiveSupport::Autoload
@@ -6,7 +8,6 @@ module ActionView #:nodoc:
autoload :AssetTagHelper
autoload :AssetUrlHelper
autoload :AtomFeedHelper
- autoload :BenchmarkHelper
autoload :CacheHelper
autoload :CaptureHelper
autoload :ControllerHelper
@@ -29,11 +30,11 @@ module ActionView #:nodoc:
extend ActiveSupport::Concern
+ include ActiveSupport::Benchmarkable
include ActiveModelHelper
include AssetTagHelper
include AssetUrlHelper
include AtomFeedHelper
- include BenchmarkHelper
include CacheHelper
include CaptureHelper
include ControllerHelper
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/benchmark_helper.rb b/actionpack/lib/action_view/helpers/benchmark_helper.rb
deleted file mode 100644
index 87fbf8f1a8..0000000000
--- a/actionpack/lib/action_view/helpers/benchmark_helper.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'active_support/benchmarkable'
-
-module ActionView
- module Helpers
- module BenchmarkHelper #:nodoc:
- include ActiveSupport::Benchmarkable
-
- def benchmark(*)
- capture { super }
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb
index 995aa10afb..8fc78ea7fb 100644
--- a/actionpack/lib/action_view/helpers/cache_helper.rb
+++ b/actionpack/lib/action_view/helpers/cache_helper.rb
@@ -167,7 +167,7 @@ module ActionView
if @virtual_path
[
*Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name),
- Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context)
+ Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context, dependencies: view_cache_dependencies)
]
else
name
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 1fbf61a5a9..10748aacf4 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -193,6 +193,7 @@ module ActionView
# * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
# dates.
# * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
+ # * <tt>:selected</tt> - Set a date that overrides the actual value.
# * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
# * <tt>:prompt</tt> - Set to true (for a generic prompt), a prompt string or a hash of prompt strings
# for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
@@ -234,6 +235,10 @@ module ActionView
# # which is initially set to the date 3 days from the current date
# date_select("article", "written_on", default: 3.days.from_now)
#
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
+ # # which is set in the form with todays date, regardless of the value in the Active Record object.
+ # date_select("article", "written_on", selected: Date.today)
+ #
# # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
# # that will have a default day of 20.
# date_select("credit_card", "bill_due", default: { day: 20 })
@@ -875,6 +880,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?
diff --git a/actionpack/lib/action_view/helpers/debug_helper.rb b/actionpack/lib/action_view/helpers/debug_helper.rb
index d361a69a92..34fc23ac1a 100644
--- a/actionpack/lib/action_view/helpers/debug_helper.rb
+++ b/actionpack/lib/action_view/helpers/debug_helper.rb
@@ -27,14 +27,12 @@ module ActionView
# new_record: true
# </pre>
def debug(object)
- begin
- Marshal::dump(object)
- object = ERB::Util.html_escape(object.to_yaml).gsub(" ", "&nbsp; ").html_safe
- content_tag(:pre, object, :class => "debug_dump")
- rescue Exception # errors from Marshal or YAML
- # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
- content_tag(:code, object.to_yaml, :class => "debug_dump")
- end
+ Marshal::dump(object)
+ object = ERB::Util.html_escape(object.to_yaml).gsub(" ", "&nbsp; ").html_safe
+ content_tag(:pre, object, :class => "debug_dump")
+ rescue Exception # errors from Marshal or YAML
+ # Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
+ content_tag(:code, object.to_yaml, :class => "debug_dump")
end
end
end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 50d3abd2fb..4a31de715d 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -84,7 +84,7 @@ module ActionView
#
# <form action="/people/256" class="edit_person" id="edit_person_256" method="post">
# <div style="margin:0;padding:0;display:inline">
- # <input name="_method" type="hidden" value="put" />
+ # <input name="_method" type="hidden" value="patch" />
# <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
# </div>
# <label for="person_first_name">First name</label>:
@@ -242,7 +242,7 @@ module ActionView
#
# is then equivalent to something like:
#
- # <%= form_for @post, as: :post, url: post_path(@post), method: :put, html: { class: "edit_post", id: "edit_post_45" } do |f| %>
+ # <%= form_for @post, as: :post, url: post_path(@post), method: :patch, html: { class: "edit_post", id: "edit_post_45" } do |f| %>
# ...
# <% end %>
#
@@ -318,7 +318,7 @@ module ActionView
#
# <form action='http://www.example.com' method='post' data-remote='true'>
# <div style='margin:0;padding:0;display:inline'>
- # <input name='_method' type='hidden' value='put' />
+ # <input name='_method' type='hidden' value='patch' />
# </div>
# ...
# </form>
@@ -336,7 +336,7 @@ module ActionView
#
# <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'>
# <div style='margin:0;padding:0;display:inline'>
- # <input name='_method' type='hidden' value='put' />
+ # <input name='_method' type='hidden' value='patch' />
# </div>
# ...
# </form>
@@ -388,9 +388,9 @@ module ActionView
# In many cases you will want to wrap the above in another helper, so you
# could do something like the following:
#
- # def labelled_form_for(record_or_name_or_array, *args, &proc)
+ # def labelled_form_for(record_or_name_or_array, *args, &block)
# options = args.extract_options!
- # form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &proc)
+ # form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &block)
# end
#
# If you don't need to attach a form to a model instance, then check out
@@ -412,10 +412,9 @@ module ActionView
# <%= form_for @invoice, url: external_url, authenticity_token: false do |f|
# ...
# <% end %>
- def form_for(record, options = {}, &proc)
+ def form_for(record, options = {}, &block)
raise ArgumentError, "Missing block" unless block_given?
-
- options[:html] ||= {}
+ html_options = options[:html] ||= {}
case record
when String, Symbol
@@ -428,15 +427,16 @@ module ActionView
apply_form_for_options!(record, object, options)
end
- options[:html][:data] = options.delete(:data) if options.has_key?(:data)
- options[:html][:remote] = options.delete(:remote) if options.has_key?(:remote)
- options[:html][:method] = options.delete(:method) if options.has_key?(:method)
- options[:html][:authenticity_token] = options.delete(:authenticity_token)
+ html_options[:data] = options.delete(:data) if options.has_key?(:data)
+ html_options[:remote] = options.delete(:remote) if options.has_key?(:remote)
+ html_options[:method] = options.delete(:method) if options.has_key?(:method)
+ html_options[:authenticity_token] = options.delete(:authenticity_token)
+
+ builder = instantiate_builder(object_name, object, options)
+ output = capture(builder, &block)
+ html_options[:multipart] = builder.multipart?
- builder = options[:parent_builder] = instantiate_builder(object_name, object, options)
- fields_for = fields_for(object_name, object, options, &proc)
- default_options = builder.form_tag_attributes
- form_tag(options[:url] || {}, default_options) { fields_for }
+ form_tag(options[:url] || {}, html_options) { output }
end
def apply_form_for_options!(record, object, options) #:nodoc:
@@ -705,9 +705,7 @@ module ActionView
# to prevent fields_for from rendering it automatically.
def fields_for(record_name, record_object = nil, options = {}, &block)
builder = instantiate_builder(record_name, record_object, options)
- output = capture(builder, &block)
- output.concat builder.hidden_field(:id) if output && options[:hidden_field_id] && !builder.emitted_hidden_id?
- output
+ capture(builder, &block)
end
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
@@ -1172,12 +1170,15 @@ module ActionView
attr_accessor :object_name, :object, :options
- attr_reader :multipart, :parent_builder, :index
+ attr_reader :multipart, :index
alias :multipart? :multipart
def multipart=(multipart)
@multipart = multipart
- parent_builder.multipart = multipart if parent_builder
+
+ if parent_builder = @options[:parent_builder]
+ parent_builder.multipart = multipart
+ end
end
def self._to_partial_path
@@ -1199,8 +1200,6 @@ module ActionView
@nested_child_index = {}
@object_name, @object, @template, @options = object_name, object, template, options
- @form_tag_attributes = options.fetch(:html, {})
- @parent_builder = options[:parent_builder]
@default_options = @options ? @options.slice(:index, :namespace) : {}
if @object_name.to_s.match(/\[\]$/)
if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
@@ -1213,11 +1212,6 @@ module ActionView
@index = options[:index] || options[:child_index]
end
- def form_tag_attributes
- options = multipart? ? { multipart: true } : {}
- options.merge! @form_tag_attributes
- end
-
(field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{selector}(method, options = {}) # def text_field(method, options = {})
@@ -1482,8 +1476,8 @@ module ActionView
def fields_for(record_name, record_object = nil, fields_options = {}, &block)
fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
fields_options[:builder] ||= options[:builder]
- fields_options[:parent_builder] = self
fields_options[:namespace] = options[:namespace]
+ fields_options[:parent_builder] = self
case record_name
when String, Symbol
@@ -1806,7 +1800,7 @@ module ActionView
association = convert_to_model(association)
if association.respond_to?(:persisted?)
- association = [association] if @object.send(association_name).is_a?(Array)
+ association = [association] if @object.send(association_name).respond_to?(:to_ary)
elsif !association.respond_to?(:to_ary)
association = @object.send(association_name)
end
@@ -1824,13 +1818,17 @@ module ActionView
end
end
- def fields_for_nested_model(name, object, options, block)
+ def fields_for_nested_model(name, object, fields_options, block)
object = convert_to_model(object)
+ emit_hidden_id = object.persisted? && fields_options.fetch(:include_id) {
+ options.fetch(:include_id, true)
+ }
- parent_include_id = self.options.fetch(:include_id, true)
- include_id = options.fetch(:include_id, parent_include_id)
- options[:hidden_field_id] = object.persisted? && include_id
- @template.fields_for(name, object, options, &block)
+ @template.fields_for(name, object, fields_options) do |f|
+ output = @template.capture(f, &block)
+ output.concat f.hidden_field(:id) if output && emit_hidden_id && !f.emitted_hidden_id?
+ output
+ end
end
def nested_child_index(name)
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index bcad05e033..ae7bfd1ec6 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -772,12 +772,12 @@ module ActionView
@template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
end
- def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {})
- @template.collection_check_boxes(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
+ 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
- def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {})
- @template.collection_radio_buttons(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
+ 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
end
end
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 479739bebd..86d9f94067 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -26,7 +26,7 @@ module ActionView
# ==== Options
# * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
# * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
- # If "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
+ # If "patch", "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
# is added to simulate the verb over post.
# * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to
# pass custom authenticity token string, or to not add authenticity_token field at all
@@ -526,19 +526,19 @@ module ActionView
#
# ==== Examples
# image_submit_tag("login.png")
- # # => <input src="/images/login.png" type="image" />
+ # # => <input alt="Login" src="/images/login.png" type="image" />
#
# image_submit_tag("purchase.png", disabled: true)
- # # => <input disabled="disabled" src="/images/purchase.png" type="image" />
+ # # => <input alt="Purchase" disabled="disabled" src="/images/purchase.png" type="image" />
#
- # image_submit_tag("search.png", class: 'search_button')
- # # => <input class="search_button" src="/images/search.png" type="image" />
+ # image_submit_tag("search.png", class: 'search_button', alt: 'Find')
+ # # => <input alt="Find" class="search_button" src="/images/search.png" type="image" />
#
# image_submit_tag("agree.png", disabled: true, class: "agree_disagree_button")
- # # => <input class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
+ # # => <input alt="Agree" class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
#
# image_submit_tag("save.png", data: { confirm: "Are you sure?" })
- # # => <input src="/images/save.png" data-confirm="Are you sure?" type="image" />
+ # # => <input alt="Save" src="/images/save.png" data-confirm="Are you sure?" type="image" />
def image_submit_tag(source, options = {})
options = options.stringify_keys
@@ -550,7 +550,7 @@ module ActionView
options["data-confirm"] = confirm
end
- tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options)
+ tag :input, { "alt" => image_alt(source), "type" => "image", "src" => path_to_image(source) }.update(options)
end
# Creates a field set for grouping HTML form elements.
diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb
index 6d99d43fb8..271a194913 100644
--- a/actionpack/lib/action_view/helpers/record_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb
@@ -65,8 +65,8 @@ module ActionView
#
# produces:
#
- # <tr id="person_123" class="person">...</tr>
- # <tr id="person_124" class="person">...</tr>
+ # <tr id="person_123" class="person">...</tr>
+ # <tr id="person_124" class="person">...</tr>
#
# content_tag_for also accepts a hash of options, which will be converted to
# additional HTML attributes. If you specify a <tt>:class</tt> value, it will be combined
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 6c400f85cb..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 = 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 aeee662071..bade121d44 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -170,7 +170,7 @@ module ActionView
# You can also use custom data attributes using the <tt>:data</tt> option:
#
# link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
- # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?"">Visit Other Site</a>
+ # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a>
def link_to(name = nil, options = nil, html_options = nil, &block)
html_options, options = options, name if block_given?
options ||= {}
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 37f93a13fc..43a88b0623 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -452,7 +452,7 @@ module ActionView
def retrieve_template_keys
keys = @locals.keys
- keys << @variable
+ keys << @variable if @object || @collection
keys << @variable_counter if @collection
keys
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index aefc42be48..b927c69260 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -1,6 +1,5 @@
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/deprecation'
require 'thread'
module ActionView
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index 8b23029bbc..1a1083bf00 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -118,7 +118,7 @@ module ActionView
private
- delegate :caching?, :to => "self.class"
+ delegate :caching?, to: :class
# This is what child classes implement. No defaults are needed
# because Resolver guarantees that the arguments are present and
diff --git a/actionpack/lib/action_view/template/types.rb b/actionpack/lib/action_view/template/types.rb
index 7611c9e708..db77cb5d19 100644
--- a/actionpack/lib/action_view/template/types.rb
+++ b/actionpack/lib/action_view/template/types.rb
@@ -1,6 +1,5 @@
require 'set'
require 'active_support/core_ext/class/attribute_accessors'
-require 'active_support/core_ext/object/blank'
module ActionView
class Template
diff --git a/actionpack/test/abstract/abstract_controller_test.rb b/actionpack/test/abstract/abstract_controller_test.rb
index 62f82a4c7a..eb9143c8f6 100644
--- a/actionpack/test/abstract/abstract_controller_test.rb
+++ b/actionpack/test/abstract/abstract_controller_test.rb
@@ -157,13 +157,11 @@ module AbstractController
private
def self.layout(formats)
+ find_template(name.underscore, {:formats => formats}, :_prefixes => ["layouts"])
+ rescue ActionView::MissingTemplate
begin
- find_template(name.underscore, {:formats => formats}, :_prefixes => ["layouts"])
+ find_template("application", {:formats => formats}, :_prefixes => ["layouts"])
rescue ActionView::MissingTemplate
- begin
- find_template("application", {:formats => formats}, :_prefixes => ["layouts"])
- rescue ActionView::MissingTemplate
- end
end
end
diff --git a/actionpack/test/abstract/helper_test.rb b/actionpack/test/abstract/helper_test.rb
index e79008fa9d..7960e5b55b 100644
--- a/actionpack/test/abstract/helper_test.rb
+++ b/actionpack/test/abstract/helper_test.rb
@@ -69,12 +69,10 @@ module AbstractController
end
def test_declare_missing_helper
- begin
- AbstractHelpers.helper :missing
- flunk "should have raised an exception"
- rescue LoadError => e
- assert_equal "helpers/missing_helper.rb", e.path
- end
+ AbstractHelpers.helper :missing
+ flunk "should have raised an exception"
+ rescue LoadError => e
+ assert_equal "helpers/missing_helper.rb", e.path
end
def test_helpers_with_module_through_block
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/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/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index b94f45bfe7..5d727b3811 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -259,7 +259,7 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
def test_flash_exist
process :flash_me
assert flash.any?
- assert_present flash['hello']
+ assert flash['hello'].present?
end
def test_flash_does_not_exist
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 2428cd7433..ca86837a2c 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -296,6 +296,24 @@ class CacheHelperOutputBufferTest < ActionController::TestCase
end
end
+class ViewCacheDependencyTest < ActionController::TestCase
+ class NoDependenciesController < ActionController::Base
+ end
+
+ class HasDependenciesController < ActionController::Base
+ view_cache_dependency { "trombone" }
+ view_cache_dependency { "flute" }
+ end
+
+ def test_view_cache_dependencies_are_empty_by_default
+ assert NoDependenciesController.new.view_cache_dependencies.empty?
+ end
+
+ def test_view_cache_dependencies_are_listed_in_declaration_order
+ assert_equal %w(trombone flute), HasDependenciesController.new.view_cache_dependencies
+ end
+end
+
class DeprecatedPageCacheExtensionTest < ActiveSupport::TestCase
def test_page_cache_extension_binds_default_static_extension
deprecation_behavior = ActiveSupport::Deprecation.behavior
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index 1c59dd5953..3b79161ad3 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -450,11 +450,9 @@ class FilterTest < ActionController::TestCase
class RescuingAroundFilterWithBlock
def around(controller)
- begin
- yield
- rescue ErrorToRescue => ex
- controller.__send__ :render, :text => "I rescued this: #{ex.inspect}"
- end
+ yield
+ rescue ErrorToRescue => ex
+ controller.__send__ :render, :text => "I rescued this: #{ex.inspect}"
end
end
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index cf561d913a..72b882539c 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -466,6 +466,58 @@ class IntegrationProcessTest < ActionDispatch::IntegrationTest
assert_equal 'http://www.example.com/foo', url_for(:controller => "foo")
end
+ def test_port_via_host!
+ with_test_route_set do
+ host! 'www.example.com:8080'
+ get '/get'
+ assert_equal 8080, request.port
+ end
+ end
+
+ def test_port_via_process
+ with_test_route_set do
+ get 'http://www.example.com:8080/get'
+ assert_equal 8080, request.port
+ end
+ end
+
+ def test_https_and_port_via_host_and_https!
+ with_test_route_set do
+ host! 'www.example.com'
+ https! true
+
+ get '/get'
+ assert_equal 443, request.port
+ assert_equal true, request.ssl?
+
+ host! 'www.example.com:443'
+ https! true
+
+ get '/get'
+ assert_equal 443, request.port
+ assert_equal true, request.ssl?
+
+ host! 'www.example.com:8443'
+ https! true
+
+ get '/get'
+ assert_equal 8443, request.port
+ assert_equal true, request.ssl?
+ end
+ end
+
+ def test_https_and_port_via_process
+ with_test_route_set do
+ get 'https://www.example.com/get'
+ assert_equal 443, request.port
+ assert_equal true, request.ssl?
+
+ get 'https://www.example.com:8443/get'
+ assert_equal 8443, request.port
+ assert_equal true, request.ssl?
+ end
+ end
+
private
def with_test_route_set
with_routing do |set|
@@ -699,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..365fa04570 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -80,7 +80,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/output_escaping_test.rb b/actionpack/test/controller/output_escaping_test.rb
index f6913a2138..43a8c05cda 100644
--- a/actionpack/test/controller/output_escaping_test.rb
+++ b/actionpack/test/controller/output_escaping_test.rb
@@ -3,7 +3,7 @@ require 'abstract_unit'
class OutputEscapingTest < ActiveSupport::TestCase
test "escape_html shouldn't die when passed nil" do
- assert_blank ERB::Util.h(nil)
+ assert ERB::Util.h(nil).blank?
end
test "escapeHTML should escape strings" do
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..8aec159499 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
@@ -68,7 +76,7 @@ class NestedParametersTest < ActiveSupport::TestCase
}
})
- permitted = params.permit :book => :genres
+ permitted = params.permit :book => {:genres => []}
assert_equal ["Tragedy"], permitted[:book][:genres]
end
@@ -124,19 +132,41 @@ class NestedParametersTest < ActiveSupport::TestCase
test "fields_for-style nested params" do
params = ActionController::Parameters.new({
- book: {
- authors_attributes: {
- :'0' => { name: 'William Shakespeare', age_of_death: '52' },
- :'-1' => { name: 'Unattributed Assistant' }
+ :book => {
+ :authors_attributes => {
+ :'0' => { :name => 'William Shakespeare', :age_of_death => '52' },
+ :'1' => { :name => 'Unattributed Assistant' },
+ :'2' => { :name => %w(injected names)}
}
}
})
- permitted = params.permit book: { authors_attributes: [ :name ] }
+ 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..303c13eb25 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -2,10 +2,131 @@ require 'abstract_unit'
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 += [StringIO.new]
+
+ 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 +194,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/render_test.rb b/actionpack/test/controller/render_test.rb
index c7a66f4298..0e5bad7482 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -793,15 +793,13 @@ class RenderTest < ActionController::TestCase
end
def test_line_offset
- begin
- get :render_line_offset
- flunk "the action should have raised an exception"
- rescue StandardError => exc
- line = exc.backtrace.first
- assert(line =~ %r{:(\d+):})
- assert_equal "1", $1,
- "The line offset is wrong, perhaps the wrong exception has been raised, exception was: #{exc.inspect}"
- end
+ get :render_line_offset
+ flunk "the action should have raised an exception"
+ rescue StandardError => exc
+ line = exc.backtrace.first
+ assert(line =~ %r{:(\d+):})
+ assert_equal "1", $1,
+ "The line offset is wrong, perhaps the wrong exception has been raised, exception was: #{exc.inspect}"
end
# :ported: compatibility
@@ -1221,27 +1219,27 @@ class RenderTest < ActionController::TestCase
def test_head_created
post :head_created
- assert_blank @response.body
+ assert @response.body.blank?
assert_response :created
end
def test_head_created_with_application_json_content_type
post :head_created_with_application_json_content_type
- assert_blank @response.body
+ assert @response.body.blank?
assert_equal "application/json", @response.header["Content-Type"]
assert_response :created
end
def test_head_ok_with_image_png_content_type
post :head_ok_with_image_png_content_type
- assert_blank @response.body
+ assert @response.body.blank?
assert_equal "image/png", @response.header["Content-Type"]
assert_response :ok
end
def test_head_with_location_header
get :head_with_location_header
- assert_blank @response.body
+ assert @response.body.blank?
assert_equal "/foo", @response.headers["Location"]
assert_response :ok
end
@@ -1254,7 +1252,7 @@ class RenderTest < ActionController::TestCase
end
get :head_with_location_object
- assert_blank @response.body
+ assert @response.body.blank?
assert_equal "http://www.nextangle.com/customers/1", @response.headers["Location"]
assert_response :ok
end
@@ -1262,14 +1260,14 @@ class RenderTest < ActionController::TestCase
def test_head_with_custom_header
get :head_with_custom_header
- assert_blank @response.body
+ assert @response.body.blank?
assert_equal "something", @response.headers["X-Custom-Header"]
assert_response :ok
end
def test_head_with_www_authenticate_header
get :head_with_www_authenticate_header
- assert_blank @response.body
+ assert @response.body.blank?
assert_equal "something", @response.headers["WWW-Authenticate"]
assert_response :ok
end
@@ -1603,7 +1601,7 @@ class LastModifiedRenderTest < ActionController::TestCase
@request.if_modified_since = @last_modified
get :conditional_hello
assert_equal 304, @response.status.to_i
- assert_blank @response.body
+ assert @response.body.blank?
assert_equal @last_modified, @response.headers['Last-Modified']
end
@@ -1618,7 +1616,7 @@ class LastModifiedRenderTest < ActionController::TestCase
@request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
get :conditional_hello
assert_equal 200, @response.status.to_i
- assert_present @response.body
+ assert @response.body.present?
assert_equal @last_modified, @response.headers['Last-Modified']
end
@@ -1632,7 +1630,7 @@ class LastModifiedRenderTest < ActionController::TestCase
@request.if_modified_since = @last_modified
get :conditional_hello_with_record
assert_equal 304, @response.status.to_i
- assert_blank @response.body
+ assert @response.body.blank?
assert_equal @last_modified, @response.headers['Last-Modified']
end
@@ -1647,7 +1645,7 @@ class LastModifiedRenderTest < ActionController::TestCase
@request.if_modified_since = 'Thu, 16 Jul 2008 00:00:00 GMT'
get :conditional_hello_with_record
assert_equal 200, @response.status.to_i
- assert_present @response.body
+ assert @response.body.present?
assert_equal @last_modified, @response.headers['Last-Modified']
end
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 1f637eb791..523a8d0572 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -320,7 +320,7 @@ class FreeCookieControllerTest < ActionController::TestCase
test 'should not emit a csrf-token meta tag' do
get :meta
- assert_blank @response.body
+ assert @response.body.blank?
end
end
diff --git a/actionpack/test/controller/required_params_test.rb b/actionpack/test/controller/required_params_test.rb
index 661bcb3945..343d57c300 100644
--- a/actionpack/test/controller/required_params_test.rb
+++ b/actionpack/test/controller/required_params_test.rb
@@ -11,20 +11,17 @@ class ActionControllerRequiredParamsTest < ActionController::TestCase
tests BooksController
test "missing required parameters will raise exception" do
- post :create, { magazine: { name: "Mjallo!" } }
- assert_response :bad_request
+ assert_raise ActionController::ParameterMissing do
+ post :create, { magazine: { name: "Mjallo!" } }
+ end
- post :create, { book: { title: "Mjallo!" } }
- assert_response :bad_request
+ assert_raise ActionController::ParameterMissing do
+ post :create, { book: { title: "Mjallo!" } }
+ end
end
test "required parameters that are present will not raise" do
post :create, { book: { name: "Mjallo!" } }
assert_response :ok
end
-
- test "missing parameters will be mentioned in the return" do
- post :create, { magazine: { name: "Mjallo!" } }
- assert_equal "Required parameter missing: book", response.body
- end
end
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index bdca1d4d77..df31338f09 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -692,7 +692,7 @@ XML
assert_equal "bar", @request.params[:foo]
@request.recycle!
post :no_op
- assert_blank @request.params[:foo]
+ assert @request.params[:foo].blank?
end
def test_symbolized_path_params_reset_after_request
@@ -931,3 +931,34 @@ class AnonymousControllerTest < ActionController::TestCase
assert_equal 'anonymous', @response.body
end
end
+
+class RoutingDefaultsTest < ActionController::TestCase
+ def setup
+ @controller = Class.new(ActionController::Base) do
+ def post
+ render :text => request.fullpath
+ end
+
+ def project
+ render :text => request.fullpath
+ end
+ end.new
+
+ @routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
+ r.draw do
+ get '/posts/:id', :to => 'anonymous#post', :bucket_type => 'post'
+ get '/projects/:id', :to => 'anonymous#project', :defaults => { :bucket_type => 'project' }
+ end
+ end
+ end
+
+ def test_route_option_can_be_passed_via_process
+ get :post, :id => 1, :bucket_type => 'post'
+ assert_equal '/posts/1', @response.body
+ end
+
+ def test_route_default_is_not_required_for_building_request_uri
+ get :project, :id => 2
+ assert_equal '/projects/2', @response.body
+ end
+end
diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb
index d3fc7128e9..ba24e7fac5 100644
--- a/actionpack/test/controller/url_for_test.rb
+++ b/actionpack/test/controller/url_for_test.rb
@@ -2,7 +2,6 @@ require 'abstract_unit'
module AbstractController
module Testing
-
class UrlForTest < ActionController::TestCase
class W
include ActionDispatch::Routing::RouteSet.new.tap { |r| r.draw { get ':controller(/:action(/:id(.:format)))' } }.url_helpers
@@ -350,10 +349,10 @@ module AbstractController
def test_with_hash_with_indifferent_access
W.default_url_options[:controller] = 'd'
W.default_url_options[:only_path] = false
- assert_equal("/c", W.new.url_for(HashWithIndifferentAccess.new('controller' => 'c', 'only_path' => true)))
+ assert_equal("/c", W.new.url_for(ActiveSupport::HashWithIndifferentAccess.new('controller' => 'c', 'only_path' => true)))
W.default_url_options[:action] = 'b'
- assert_equal("/c/a", W.new.url_for(HashWithIndifferentAccess.new('controller' => 'c', 'action' => 'a', 'only_path' => true)))
+ assert_equal("/c/a", W.new.url_for(ActiveSupport::HashWithIndifferentAccess.new('controller' => 'c', 'action' => 'a', 'only_path' => true)))
end
def test_url_params_with_nil_to_param_are_not_in_url
diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb
index c0b9833603..0480295056 100644
--- a/actionpack/test/controller/webservice_test.rb
+++ b/actionpack/test/controller/webservice_test.rb
@@ -116,22 +116,22 @@ class WebServiceTest < ActionDispatch::IntegrationTest
end
end
- def test_register_and_use_yaml
+ def test_post_xml_using_a_disallowed_type_attribute
+ $stderr = StringIO.new
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'}
+ post '/', '<foo type="symbol">value</foo>', 'CONTENT_TYPE' => 'application/xml'
+ assert_response 500
- assert_equal 'entry', @controller.response.body
- assert @controller.params.has_key?(:entry)
- assert_equal 'loaded from yaml', @controller.params["entry"]
- end
+ post '/', '<foo type="yaml">value</foo>', 'CONTENT_TYPE' => 'application/xml'
+ assert_response 500
end
+ ensure
+ $stderr = STDERR
end
- def test_register_and_use_yaml_as_symbol
+ def test_register_and_use_yaml
with_test_route_set do
- with_params_parsers Mime::YAML => :yaml 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'}
@@ -211,36 +211,6 @@ class WebServiceTest < ActionDispatch::IntegrationTest
end
end
- def test_typecast_as_yaml
- with_test_route_set do
- with_params_parsers Mime::YAML => :yaml do
- yaml = (<<-YAML).strip
- ---
- data:
- a: 15
- b: false
- c: true
- d: 2005-03-17
- e: 2005-03-17T21:41:07Z
- f: unparsed
- g:
- - 1
- - hello
- - 1974-07-25
- YAML
- post "/", yaml, {'CONTENT_TYPE' => 'application/x-yaml'}
- params = @controller.params
- assert_equal 15, params[:data][:a]
- assert_equal false, params[:data][:b]
- assert_equal true, params[:data][:c]
- assert_equal Date.new(2005,3,17), params[:data][:d]
- assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e]
- assert_equal "unparsed", params[:data][:f]
- assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g]
- end
- end
- end
-
private
def with_params_parsers(parsers = {})
old_session = @integration_session
diff --git a/actionpack/test/dispatch/best_standards_support_test.rb b/actionpack/test/dispatch/best_standards_support_test.rb
index 0737c40a39..551bb9621a 100644
--- a/actionpack/test/dispatch/best_standards_support_test.rb
+++ b/actionpack/test/dispatch/best_standards_support_test.rb
@@ -16,9 +16,10 @@ class BestStandardsSupportTest < ActiveSupport::TestCase
assert_equal nil, headers["X-UA-Compatible"]
end
- def test_appends_to_app_headers
+ 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"]
diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb
index 39e791b4f4..6035f0361e 100644
--- a/actionpack/test/dispatch/debug_exceptions_test.rb
+++ b/actionpack/test/dispatch/debug_exceptions_test.rb
@@ -39,14 +39,25 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
raise ActionController::BadRequest
when "/missing_keys"
raise ActionController::UrlGenerationError, "No route matches"
+ when "/parameter_missing"
+ raise ActionController::ParameterMissing, :missing_param_key
else
raise "puke!"
end
end
end
- ProductionApp = ActionDispatch::DebugExceptions.new(Boomer.new(false))
- DevelopmentApp = ActionDispatch::DebugExceptions.new(Boomer.new(true))
+ def setup
+ app = ActiveSupport::OrderedOptions.new
+ app.config = ActiveSupport::OrderedOptions.new
+ app.config.assets = ActiveSupport::OrderedOptions.new
+ app.config.assets.prefix = '/sprockets'
+ Rails.stubs(:application).returns(app)
+ end
+
+ RoutesApp = Struct.new(:routes).new(SharedTestRoutes)
+ ProductionApp = ActionDispatch::DebugExceptions.new(Boomer.new(false), RoutesApp)
+ DevelopmentApp = ActionDispatch::DebugExceptions.new(Boomer.new(true), RoutesApp)
test 'skip diagnosis if not showing detailed exceptions' do
@app = ProductionApp
@@ -78,6 +89,15 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
assert boomer.closed, "Expected to close the response body"
end
+ test 'displays routes in a table when a RoutingError occurs' do
+ @app = DevelopmentApp
+ get "/pass", {}, {'action_dispatch.show_exceptions' => true}
+ routing_table = body[/route_table.*<.table>/m]
+ assert_match '/:controller(/:action)(.:format)', routing_table
+ assert_match ':controller#:action', routing_table
+ assert_no_match '&lt;|&gt;', routing_table, "there should not be escaped html in the output"
+ end
+
test "rescue with diagnostics message" do
@app = DevelopmentApp
@@ -96,6 +116,10 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
get "/bad_request", {}, {'action_dispatch.show_exceptions' => true}
assert_response 400
assert_match(/ActionController::BadRequest/, body)
+
+ get "/parameter_missing", {}, {'action_dispatch.show_exceptions' => true}
+ assert_response 400
+ assert_match(/ActionController::ParameterMissing/, body)
end
test "does not show filtered parameters" do
diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb
index c0c3147e37..7d3fc84089 100644
--- a/actionpack/test/dispatch/request/json_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb
@@ -30,6 +30,21 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest
)
end
+ test "nils are stripped from collections" do
+ assert_parses(
+ {"person" => nil},
+ "{\"person\":[null]}", { 'CONTENT_TYPE' => 'application/json' }
+ )
+ assert_parses(
+ {"person" => ['foo']},
+ "{\"person\":[\"foo\",null]}", { 'CONTENT_TYPE' => 'application/json' }
+ )
+ assert_parses(
+ {"person" => nil},
+ "{\"person\":[null, null]}", { 'CONTENT_TYPE' => 'application/json' }
+ )
+ end
+
test "logs error if parsing unsuccessful" do
with_test_routing do
output = StringIO.new
@@ -107,6 +122,13 @@ class RootLessJSONParamsParsingTest < ActionDispatch::IntegrationTest
)
end
+ test "parses json with non-object JSON content" do
+ assert_parses(
+ {"user" => {"_json" => "string content" }, "_json" => "string content" },
+ "\"string content\"", { 'CONTENT_TYPE' => 'application/json' }
+ )
+ end
+
private
def assert_parses(expected, actual, headers = {})
with_test_routing(UsersController) do
diff --git a/actionpack/test/dispatch/request/query_string_parsing_test.rb b/actionpack/test/dispatch/request/query_string_parsing_test.rb
index 3cb430d83d..f072a9f717 100644
--- a/actionpack/test/dispatch/request/query_string_parsing_test.rb
+++ b/actionpack/test/dispatch/request/query_string_parsing_test.rb
@@ -84,8 +84,8 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest
assert_parses({"action" => nil}, "action")
assert_parses({"action" => {"foo" => nil}}, "action[foo]")
assert_parses({"action" => {"foo" => { "bar" => nil }}}, "action[foo][bar]")
- assert_parses({"action" => {"foo" => { "bar" => [] }}}, "action[foo][bar][]")
- assert_parses({"action" => {"foo" => []}}, "action[foo][]")
+ assert_parses({"action" => {"foo" => { "bar" => nil }}}, "action[foo][bar][]")
+ assert_parses({"action" => {"foo" => nil }}, "action[foo][]")
assert_parses({"action"=>{"foo"=>[{"bar"=>nil}]}}, "action[foo][][bar]")
end
diff --git a/actionpack/test/dispatch/request/xml_params_parsing_test.rb b/actionpack/test/dispatch/request/xml_params_parsing_test.rb
index cb68667002..f13b64a3c7 100644
--- a/actionpack/test/dispatch/request/xml_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/xml_params_parsing_test.rb
@@ -30,6 +30,23 @@ class XmlParamsParsingTest < ActionDispatch::IntegrationTest
assert_equal "<ok>bar</ok>", resp.body
end
+ def assert_parses(expected, xml)
+ with_test_routing do
+ post "/parse", xml, default_headers
+ assert_response :ok
+ assert_equal(expected, TestController.last_request_parameters)
+ end
+ end
+
+ test "nils are stripped from collections" do
+ assert_parses(
+ {"hash" => { "person" => nil} },
+ "<hash><person type=\"array\"><person nil=\"true\"/></person></hash>")
+ assert_parses(
+ {"hash" => { "person" => ['foo']} },
+ "<hash><person type=\"array\"><person>foo</person><person nil=\"true\"/></person>\n</hash>")
+ end
+
test "parses hash params" do
with_test_routing do
xml = "<person><name>David</name></person>"
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 02675c7f8c..8a01b29340 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -462,6 +462,27 @@ class RequestTest < ActiveSupport::TestCase
assert request.put?
end
+ test "post uneffected by local inflections" do
+ existing_acrnoyms = ActiveSupport::Inflector.inflections.acronyms.dup
+ existing_acrnoym_regex = ActiveSupport::Inflector.inflections.acronym_regex.dup
+ begin
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.acronym "POS"
+ end
+ assert_equal "pos_t", "POST".underscore
+ request = stub_request "REQUEST_METHOD" => "POST"
+ assert_equal :post, ActionDispatch::Request::HTTP_METHOD_LOOKUP["POST"]
+ assert_equal :post, request.method_symbol
+ assert request.post?
+ ensure
+ # Reset original acronym set
+ ActiveSupport::Inflector.inflections do |inflect|
+ inflect.send(:instance_variable_set,"@acronyms",existing_acrnoyms)
+ inflect.send(:instance_variable_set,"@acronym_regex",existing_acrnoym_regex)
+ end
+ end
+ end
+
test "xml format" do
request = stub_request
request.expects(:parameters).at_least_once.returns({ :format => 'xml' })
@@ -566,6 +587,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/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb
index c058bd4909..55221f87c4 100644
--- a/actionpack/test/dispatch/routing/inspector_test.rb
+++ b/actionpack/test/dispatch/routing/inspector_test.rb
@@ -1,5 +1,4 @@
-require 'active_support/testing/autorun'
-require 'action_controller'
+require 'abstract_unit'
require 'rails/engine'
require 'action_dispatch/routing/inspector'
@@ -8,7 +7,6 @@ module ActionDispatch
class RoutesInspectorTest < ActiveSupport::TestCase
def setup
@set = ActionDispatch::Routing::RouteSet.new
- @inspector = ActionDispatch::Routing::RoutesInspector.new
app = ActiveSupport::OrderedOptions.new
app.config = ActiveSupport::OrderedOptions.new
app.config.assets = ActiveSupport::OrderedOptions.new
@@ -17,9 +15,18 @@ module ActionDispatch
Rails.stubs(:env).returns("development")
end
- def draw(&block)
+ def draw(options = {}, &block)
@set.draw(&block)
- @inspector.format(@set.routes)
+ inspector = ActionDispatch::Routing::RoutesInspector.new(@set.routes)
+ 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
@@ -40,7 +47,8 @@ module ActionDispatch
expected = [
"custom_assets GET /custom/assets(.:format) custom_assets#show",
" blog /blog Blog::Engine",
- "\nRoutes for Blog::Engine:",
+ "",
+ "Routes for Blog::Engine:",
"cart GET /cart(.:format) cart#show"
]
assert_equal expected, output
@@ -165,6 +173,22 @@ module ActionDispatch
assert_equal " bar GET /bar(.:format) redirect(307, path: /foo/bar)", output[1]
assert_equal "foobar GET /foobar(.:format) redirect(301)", output[2]
end
+
+ def test_routes_can_be_filtered
+ output = draw(filter: 'posts') do
+ resources :articles
+ resources :posts
+ end
+
+ assert_equal [" posts GET /posts(.:format) posts#index",
+ " POST /posts(.:format) posts#create",
+ " new_post GET /posts/new(.:format) posts#new",
+ "edit_post GET /posts/:id/edit(.:format) posts#edit",
+ " post GET /posts/:id(.:format) posts#show",
+ " PATCH /posts/:id(.:format) posts#update",
+ " PUT /posts/:id(.:format) posts#update",
+ " DELETE /posts/:id(.:format) posts#destroy"], output
+ end
end
end
end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index cb5299e8d3..9f31ce8127 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -2678,6 +2678,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)
@@ -3252,3 +3261,79 @@ class TestOptionalRootSegments < ActionDispatch::IntegrationTest
assert_equal '/page/1', root_path(:page => '1')
end
end
+
+class TestPortConstraints < ActionDispatch::IntegrationTest
+ Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
+ app.draw do
+ ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
+
+ get '/integer', to: ok, constraints: { :port => 8080 }
+ get '/string', to: ok, constraints: { :port => '8080' }
+ get '/array', to: ok, constraints: { :port => [8080] }
+ get '/regexp', to: ok, constraints: { :port => /8080/ }
+ end
+ end
+
+ include Routes.url_helpers
+ def app; Routes end
+
+ def test_integer_port_constraints
+ get 'http://www.example.com/integer'
+ assert_response :not_found
+
+ get 'http://www.example.com:8080/integer'
+ assert_response :success
+ end
+
+ def test_string_port_constraints
+ get 'http://www.example.com/string'
+ assert_response :not_found
+
+ get 'http://www.example.com:8080/string'
+ assert_response :success
+ end
+
+ def test_array_port_constraints
+ get 'http://www.example.com/array'
+ assert_response :not_found
+
+ get 'http://www.example.com:8080/array'
+ assert_response :success
+ end
+
+ def test_regexp_port_constraints
+ get 'http://www.example.com/regexp'
+ assert_response :not_found
+
+ get 'http://www.example.com:8080/regexp'
+ assert_response :success
+ end
+end
+
+class TestRouteDefaults < ActionDispatch::IntegrationTest
+ stub_controllers do |routes|
+ Routes = routes
+ Routes.draw do
+ resources :posts, bucket_type: 'post'
+ resources :projects, defaults: { bucket_type: 'project' }
+ end
+ end
+
+ def app
+ Routes
+ end
+
+ include Routes.url_helpers
+
+ def test_route_options_are_required_for_url_for
+ assert_raises(ActionController::UrlGenerationError) do
+ assert_equal '/posts/1', url_for(controller: 'posts', action: 'show', id: 1, only_path: true)
+ end
+
+ assert_equal '/posts/1', url_for(controller: 'posts', action: 'show', id: 1, bucket_type: 'post', only_path: true)
+ end
+
+ def test_route_defaults_are_not_required_for_url_for
+ assert_equal '/projects/1', url_for(controller: 'projects', action: 'show', id: 1, only_path: true)
+ end
+end
diff --git a/actionpack/test/dispatch/ssl_test.rb b/actionpack/test/dispatch/ssl_test.rb
index b4a39219bf..a9bea7ea73 100644
--- a/actionpack/test/dispatch/ssl_test.rb
+++ b/actionpack/test/dispatch/ssl_test.rb
@@ -57,6 +57,13 @@ class SSLTest < ActionDispatch::IntegrationTest
response.headers['Strict-Transport-Security']
end
+ def test_hsts_expires_with_duration
+ self.app = ActionDispatch::SSL.new(default_app, :hsts => { :expires => 1.year })
+ get "https://example.org/"
+ assert_equal "max-age=31557600",
+ response.headers['Strict-Transport-Security']
+ end
+
def test_hsts_include_subdomains
self.app = ActionDispatch::SSL.new(default_app, :hsts => { :subdomains => true })
get "https://example.org/"
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/_partial_name_local_variable.erb b/actionpack/test/fixtures/test/_partial_name_local_variable.erb
new file mode 100644
index 0000000000..cc3a91c89f
--- /dev/null
+++ b/actionpack/test/fixtures/test/_partial_name_local_variable.erb
@@ -0,0 +1 @@
+<%= partial_name_local_variable %>
diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb
index 78608a5c6b..cbe6284714 100644
--- a/actionpack/test/journey/route_test.rb
+++ b/actionpack/test/journey/route_test.rb
@@ -6,18 +6,18 @@ module ActionDispatch
def test_initialize
app = Object.new
path = Path::Pattern.new '/:controller(/:action(/:id(.:format)))'
- defaults = Object.new
+ defaults = {}
route = Route.new("name", app, path, {}, defaults)
assert_equal app, route.app
assert_equal path, route.path
- assert_equal defaults, route.defaults
+ assert_same defaults, route.defaults
end
def test_route_adds_itself_as_memo
app = Object.new
path = Path::Pattern.new '/:controller(/:action(/:id(.:format)))'
- defaults = Object.new
+ defaults = {}
route = Route.new("name", app, path, {}, defaults)
route.ast.grep(Nodes::Terminal).each do |node|
@@ -82,11 +82,14 @@ module ActionDispatch
end
def test_score
+ constraints = {:required_defaults => [:controller, :action]}
+ defaults = {:controller=>"pages", :action=>"show"}
+
path = Path::Pattern.new "/page/:id(/:action)(.:format)"
- specific = Route.new "name", nil, path, {}, {:controller=>"pages", :action=>"show"}
+ specific = Route.new "name", nil, path, constraints, defaults
path = Path::Pattern.new "/:controller(/:action(/:id))(.:format)"
- generic = Route.new "name", nil, path, {}
+ generic = Route.new "name", nil, path, constraints
knowledge = {:id=>20, :controller=>"pages", :action=>"show"}
diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb
index 27bdb0108a..3d52b2e9ee 100644
--- a/actionpack/test/journey/router_test.rb
+++ b/actionpack/test/journey/router_test.rb
@@ -155,7 +155,7 @@ module ActionDispatch
Router::Strexp.new("/foo/:id", { :id => /\d/ }, ['/', '.', '?'], false)
]
- assert_raises(Router::RoutingError) do
+ assert_raises(ActionController::UrlGenerationError) do
@formatter.generate(:path_info, nil, { :id => '10' }, { })
end
end
@@ -168,7 +168,7 @@ module ActionDispatch
path, _ = @formatter.generate(:path_info, nil, { :id => '10' }, { })
assert_equal '/foo/10', path
- assert_raises(Router::RoutingError) do
+ assert_raises(ActionController::UrlGenerationError) do
@formatter.generate(:path_info, nil, { :id => 'aa' }, { })
end
end
@@ -194,11 +194,11 @@ module ActionDispatch
path = Path::Pattern.new pattern
@router.routes.add_route nil, path, {}, {}, route_name
- error = assert_raises(Router::RoutingError) do
+ error = assert_raises(ActionController::UrlGenerationError) do
@formatter.generate(:path_info, route_name, { }, { })
end
- assert_match(/required keys: \[:id\]/, error.message)
+ assert_match(/missing required keys: \[:id\]/, error.message)
end
def test_X_Cascade
diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb
index 89aae4ac56..63b5ac0fab 100644
--- a/actionpack/test/template/atom_feed_helper_test.rb
+++ b/actionpack/test/template/atom_feed_helper_test.rb
@@ -238,7 +238,7 @@ class AtomFeedTest < ActionController::TestCase
get :index, :id=>"provide_builder"
# because we pass in the non-default builder, the content generated by the
# helper should go 'nowhere'. Leaving the response body blank.
- assert_blank @response.body
+ assert @response.body.blank?
end
end
diff --git a/actionpack/test/template/benchmark_helper_test.rb b/actionpack/test/template/benchmark_helper_test.rb
deleted file mode 100644
index 8c198d2562..0000000000
--- a/actionpack/test/template/benchmark_helper_test.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require 'abstract_unit'
-require 'stringio'
-
-class BenchmarkHelperTest < ActionView::TestCase
- include RenderERBUtils
- tests ActionView::Helpers::BenchmarkHelper
-
- def test_output_in_erb
- output = render_erb("Hello <%= benchmark do %>world<% end %>")
- expected = 'Hello world'
- assert_equal expected, output
- end
-
- def test_returns_value_from_block
- assert_equal 'test', benchmark { 'test' }
- end
-
- def test_default_message
- log = StringIO.new
- self.stubs(:logger).returns(Logger.new(log))
- benchmark {}
- assert_match(/Benchmarking \(\d+.\d+ms\)/, log.rewind && log.read)
- 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 f9ce63fcb0..f11adefad8 100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -1510,6 +1510,44 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, date_select("post", "written_on")
end
+
+ def test_date_select_with_selected
+ @post = Post.new
+ @post.written_on = Date.new(2004, 6, 15)
+
+ expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]">\n}
+ expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_written_on_2i" name="post[written_on(2i)]">\n}
+ expected << %{<option value="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" selected="selected">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="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" selected="selected">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", :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
@post = Post.new
@@ -1968,6 +2006,44 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, time_select("post", "written_on")
end
+ def test_time_select_with_selected
+ @post = Post.new
+ @post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
+
+ expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}
+ expected << %{<input type="hidden" id="post_written_on_2i" name="post[written_on(2i)]" value="6" />\n}
+ expected << %{<input type="hidden" id="post_written_on_3i" name="post[written_on(3i)]" value="15" />\n}
+
+ expected << %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n)
+ 0.upto(23) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 12}>#{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)}"#{' 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))
+ 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
@post = Post.new
@post.written_on = Time.local(2004, 6, 15, 15, 16, 35)
@@ -2165,6 +2241,62 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, datetime_select("post", "updated_at")
end
+ def test_datetime_select_with_selected
+ @post = Post.new
+ @post.updated_at = Time.local(2004, 6, 15, 16, 35)
+
+ expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]">\n}
+ expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n}
+ expected << "</select>\n"
+
+ expected << %{<select id="post_updated_at_2i" name="post[updated_at(2i)]">\n}
+ expected << %{<option value="1">January</option>\n<option value="2">February</option>\n<option value="3" selected="selected">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" selected="selected">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" selected="selected">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" selected="selected">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", :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/digestor_test.rb b/actionpack/test/template/digestor_test.rb
index 02b1fd87a8..849e2981a6 100644
--- a/actionpack/test/template/digestor_test.rb
+++ b/actionpack/test/template/digestor_test.rb
@@ -138,6 +138,20 @@ class TemplateDigestorTest < ActionView::TestCase
end
end
+ def test_dependencies_via_options_results_in_different_digest
+ digest_plain = digest("comments/_comment")
+ digest_fridge = digest("comments/_comment", dependencies: ["fridge"])
+ digest_phone = digest("comments/_comment", dependencies: ["phone"])
+ digest_fridge_phone = digest("comments/_comment", dependencies: ["fridge", "phone"])
+
+ assert_not_equal digest_plain, digest_fridge
+ assert_not_equal digest_plain, digest_phone
+ assert_not_equal digest_plain, digest_fridge_phone
+ assert_not_equal digest_fridge, digest_phone
+ assert_not_equal digest_fridge, digest_fridge_phone
+ assert_not_equal digest_phone, digest_fridge_phone
+ end
+
private
def assert_logged(message)
old_logger = ActionView::Base.logger
@@ -164,8 +178,8 @@ class TemplateDigestorTest < ActionView::TestCase
ActionView::Digestor.cache.clear
end
- def digest(template_name)
- ActionView::Digestor.digest(template_name, :html, FixtureFinder.new)
+ def digest(template_name, options={})
+ ActionView::Digestor.digest(template_name, :html, FixtureFinder.new, options)
end
def change_template(template_name)
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index c730e3ab74..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' />" +
@@ -1120,15 +1199,64 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ 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 }
+ end
+ concat rendered_radio_buttons
+ end
+
+ 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>" +
+ "<label for='post_active_false'>"+
+ "<input checked='checked' id='post_active_false' name='post[active]' type='radio' value='false' />" +
+ "false</label>"
+ end
+
+ 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' />" +
@@ -1141,14 +1269,72 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ 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}"] }
+ 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
+ end
+
+ 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>" +
+ "<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='' />"
+ end
+
+ 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
@@ -1164,20 +1350,19 @@ 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
assert_dom_equal expected, output_buffer
end
-
def test_form_for_with_format
- form_for(@post, :format => :json, :html => { :id => "edit_post_123", :class => "edit_post" }) do |f|
+ 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
@@ -1192,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
@@ -1201,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>" +
@@ -1222,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' />" +
@@ -1239,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' />" +
@@ -1258,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
@@ -1270,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' />" +
@@ -1287,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' />" +
@@ -1306,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' />" +
@@ -1323,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)
@@ -1340,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>" +
@@ -1359,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' />" +
@@ -1377,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' />"
@@ -1395,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' />"
@@ -1411,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
@@ -1424,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' />" +
@@ -1441,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
@@ -1449,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
@@ -1463,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
@@ -1490,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|
@@ -1498,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' />"
@@ -1532,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
@@ -1545,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
@@ -1560,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
@@ -1581,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
@@ -1589,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
@@ -1605,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
@@ -1621,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
@@ -1635,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
@@ -1649,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
@@ -1664,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
@@ -1677,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
@@ -1691,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
@@ -1722,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
@@ -1749,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" />'
@@ -1768,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" />'
@@ -1782,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
@@ -1798,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
@@ -1816,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" />'
@@ -1843,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" />'
@@ -1864,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" />' +
@@ -1885,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" />' +
@@ -1906,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)
@@ -1918,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" />' +
@@ -1932,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|
@@ -1944,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" />' +
@@ -1967,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" />' +
@@ -1991,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" />' +
@@ -2014,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" />'
@@ -2035,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" />' +
@@ -2053,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
@@ -2070,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" />' +
@@ -2091,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" />' +
@@ -2113,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" />' +
@@ -2136,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" />' +
@@ -2151,12 +2336,35 @@ 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
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ class FakeAssociationProxy
+ def to_ary
+ [1, 2, 3]
+ end
+ end
+
+ def test_nested_fields_for_with_child_index_option_override_on_a_nested_attributes_collection_association_with_proxy
+ @post.comments = FakeAssociationProxy.new
+
+ form_for(@post) do |f|
+ 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
'<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
@@ -2208,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
@@ -2242,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" />' +
@@ -2269,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
@@ -2309,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)
@@ -2325,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)
@@ -2384,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
@@ -2399,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)
@@ -2408,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' />" +
@@ -2419,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)
@@ -2428,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' />"
@@ -2444,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
@@ -2462,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/>"
@@ -2487,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/>"
@@ -2506,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
@@ -2516,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)
@@ -2533,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
''
@@ -2546,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
@@ -2559,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
@@ -2572,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
@@ -2583,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
@@ -2631,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
@@ -2646,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
@@ -2658,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
@@ -2693,6 +2901,19 @@ class FormHelperTest < ActionView::TestCase
end
end
+ def test_form_for_only_instantiates_builder_once
+ initialization_count = 0
+ builder_class = Class.new(ActionView::Helpers::FormBuilder) do
+ define_method :initialize do |*args|
+ super(*args)
+ initialization_count += 1
+ end
+ end
+
+ form_for(@post, builder: builder_class) { }
+ assert_equal 1, initialization_count, 'form builder instantiated more than once'
+ end
+
protected
def hidden_fields(method = nil)
@@ -2714,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/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index 0a94fa079b..6c6a142397 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -488,7 +488,7 @@ class FormTagHelperTest < ActionView::TestCase
def test_image_submit_tag_with_confirmation
assert_dom_equal(
- %(<input type="image" src="/images/save.gif" data-confirm="Are you sure?" />),
+ %(<input alt="Save" type="image" src="/images/save.gif" data-confirm="Are you sure?" />),
image_submit_tag("save.gif", :data => { :confirm => "Are you sure?" })
)
end
@@ -496,7 +496,7 @@ class FormTagHelperTest < ActionView::TestCase
def test_image_submit_tag_with_deprecated_confirmation
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use 'data: { confirm: \'Text\' }' instead" do
assert_dom_equal(
- %(<input type="image" src="/images/save.gif" data-confirm="Are you sure?" />),
+ %(<input alt="Save" type="image" src="/images/save.gif" data-confirm="Are you sure?" />),
image_submit_tag("save.gif", :confirm => "Are you sure?")
)
end
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 9fb26e32b1..8111e58527 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -318,6 +318,13 @@ module RenderTestCases
@controller_view.render(customers, :greeting => "Hello")
end
+ def test_render_partial_without_object_or_collection_does_not_generate_partial_name_local_variable
+ exception = assert_raises ActionView::Template::Error do
+ @controller_view.render("partial_name_local_variable")
+ end
+ assert_match "undefined local variable or method `partial_name_local_variable'", exception.message
+ end
+
# TODO: The reason for this test is unclear, improve documentation
def test_render_partial_and_fallback_to_layout
assert_equal "Before (Josh)\n\nAfter", @view.render(:partial => "test/layout_for_partial", :locals => { :name => "Josh" })
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc
index 3262bf61ac..1b1fe2fa2b 100644
--- a/activemodel/README.rdoc
+++ b/activemodel/README.rdoc
@@ -1,7 +1,7 @@
= Active Model -- model interfaces for Rails
Active Model provides a known set of interfaces for usage in model classes.
-They allow for Action Pack helpers to interact with non-ActiveRecord models,
+They allow for Action Pack helpers to interact with non-Active Record models,
for example. Active Model also helps building custom ORMs for use outside of
the Rails framework.
@@ -11,7 +11,7 @@ code from Rails, or monkey patch entire helpers to make them handle objects
that did not exactly conform to the Active Record interface. This would result
in code duplication and fragile applications that broke on upgrades. Active
Model solves this by defining an explicit API. You can read more about the
-API in ActiveModel::Lint::Tests.
+API in <tt>ActiveModel::Lint::Tests</tt>.
Active Model provides a default module that implements the basic API required
to integrate with Action Pack out of the box: <tt>ActiveModel::Model</tt>.
@@ -133,24 +133,6 @@ behavior out of the box:
{Learn more}[link:classes/ActiveModel/Naming.html]
-* Observer support
-
- ActiveModel::Observers allows your object to implement the Observer
- pattern in a Rails App and take advantage of all the standard observer
- functions.
-
- class PersonObserver < ActiveModel::Observer
- def after_create(person)
- person.logger.info("New person added!")
- end
-
- def after_destroy(person)
- person.logger.warn("Person with an id of #{person.id} was destroyed!")
- end
- end
-
- {Learn more}[link:classes/ActiveModel/Observer.html]
-
* Making objects serializable
ActiveModel::Serialization provides a standard interface for your object
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index db5759ada9..6d11c0fbdc 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -436,7 +436,7 @@ module ActiveModel
# attribute_missing is like method_missing, but for attributes. When method_missing is
# called we check to see if there is a matching attribute method. If so, we call
# attribute_missing to dispatch the attribute. This method can be overloaded to
- # customise the behaviour.
+ # customize the behavior.
def attribute_missing(match, *args, &block)
__send__(match.target, match.attr_name, *args, &block)
end
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index ecb7a9e9b1..6e67cd2285 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -119,7 +119,7 @@ module ActiveModel
# person.name = 'bob'
# person.changes # => { "name" => ["bill", "bob"] }
def changes
- HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
+ ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
end
# Returns a hash of attributes that were changed before the model was saved.
diff --git a/activemodel/lib/active_model/validations/clusivity.rb b/activemodel/lib/active_model/validations/clusivity.rb
index 3d7067fbcb..49df98d6c1 100644
--- a/activemodel/lib/active_model/validations/clusivity.rb
+++ b/activemodel/lib/active_model/validations/clusivity.rb
@@ -3,7 +3,7 @@ require 'active_support/core_ext/range'
module ActiveModel
module Validations
module Clusivity #:nodoc:
- ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " <<
+ ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " \
"and must be supplied as the :in (or :within) option of the configuration hash"
def check_validity!
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 744c196d30..085532c35b 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -17,9 +17,9 @@ module ActiveModel
end
def validate_each(record, attr_name, value)
- before_type_cast = "#{attr_name}_before_type_cast"
+ before_type_cast = :"#{attr_name}_before_type_cast"
- raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym)
+ raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast)
raw_value ||= value
return if options[:allow_nil] && raw_value.nil?
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index d51f4d1936..f989b21140 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -103,7 +103,7 @@ module ActiveModel
end
# Accepts options that will be made available through the +options+ reader.
- def initialize(options)
+ def initialize(options = {})
@options = options.freeze
end
diff --git a/activemodel/test/cases/conversion_test.rb b/activemodel/test/cases/conversion_test.rb
index d679ad41aa..a037666cbc 100644
--- a/activemodel/test/cases/conversion_test.rb
+++ b/activemodel/test/cases/conversion_test.rb
@@ -29,4 +29,8 @@ class ConversionTest < ActiveModel::TestCase
assert_equal "helicopters/helicopter", Helicopter.new.to_partial_path,
"ActiveModel::Conversion#to_partial_path caching should be class-specific"
end
+
+ test "to_partial_path handles namespaced models" do
+ assert_equal "helicopter/comanches/comanche", Helicopter::Comanche.new.to_partial_path
+ end
end
diff --git a/activemodel/test/cases/dirty_test.rb b/activemodel/test/cases/dirty_test.rb
index 0b9f9537e2..ba45089cca 100644
--- a/activemodel/test/cases/dirty_test.rb
+++ b/activemodel/test/cases/dirty_test.rb
@@ -46,7 +46,7 @@ class DirtyTest < ActiveModel::TestCase
assert @model.name_changed?
end
- test "list of changed attributes" do
+ test "list of changed attribute keys" do
assert_equal [], @model.changed
@model.name = "Paul"
assert_equal ['name'], @model.changed
@@ -106,6 +106,17 @@ class DirtyTest < ActiveModel::TestCase
assert_equal [nil, "Jericho Cane"], @model.previous_changes['name']
end
+ test "previous value is preserved when changed after save" do
+ assert_equal({}, @model.changed_attributes)
+ @model.name = "Paul"
+ assert_equal({ "name" => nil }, @model.changed_attributes)
+
+ @model.save
+
+ @model.name = "John"
+ assert_equal({ "name" => "Paul" }, @model.changed_attributes)
+ end
+
test "changing the same attribute multiple times retains the correct original value" do
@model.name = "Otto"
@model.save
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index 1ffce1ae47..cc0c3f16d2 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -54,6 +54,59 @@ class ErrorsTest < ActiveModel::TestCase
assert errors.has_key?(:foo), 'errors should have key :foo'
end
+ test "should be able to clear the errors" do
+ person = Person.new
+ person.validate!
+
+ assert_equal 1, person.errors.count
+ person.errors.clear
+ assert person.errors.empty?
+ end
+
+ test "get returns the error by the provided key" do
+ errors = ActiveModel::Errors.new(self)
+ errors[:foo] = "omg"
+
+ assert_equal ["omg"], errors.get(:foo)
+ end
+
+ test "sets the error with the provided key" do
+ errors = ActiveModel::Errors.new(self)
+ errors.set(:foo, "omg")
+
+ assert_equal({ foo: "omg" }, errors.messages)
+ end
+
+ test "values returns an array of messages" do
+ errors = ActiveModel::Errors.new(self)
+ errors.set(:foo, "omg")
+ errors.set(:baz, "zomg")
+
+ assert_equal ["omg", "zomg"], errors.values
+ end
+
+ test "keys returns the error keys" do
+ errors = ActiveModel::Errors.new(self)
+ errors.set(:foo, "omg")
+ errors.set(:baz, "zomg")
+
+ assert_equal [:foo, :baz], errors.keys
+ end
+
+ test "as_json returns a json formatted representation of the errors hash" do
+ person = Person.new
+ person.validate!
+
+ assert_equal({ name: ["can not be nil"] }, person.errors.as_json)
+ end
+
+ test "as_json with :full_messages option" do
+ person = Person.new
+ person.validate!
+
+ assert_equal({ name: ["name can not be nil"] }, person.errors.as_json(full_messages: true))
+ end
+
test "should return true if no errors" do
person = Person.new
person.errors[:foo]
diff --git a/activemodel/test/cases/model_test.rb b/activemodel/test/cases/model_test.rb
index d93fd96b88..588d8e661e 100644
--- a/activemodel/test/cases/model_test.rb
+++ b/activemodel/test/cases/model_test.rb
@@ -20,7 +20,13 @@ class ModelTest < ActiveModel::TestCase
def test_initialize_with_nil_or_empty_hash_params_does_not_explode
assert_nothing_raised do
BasicModel.new()
+ BasicModel.new nil
BasicModel.new({})
end
end
+
+ def test_persisted_is_always_false
+ object = BasicModel.new(:attr => "value")
+ assert object.persisted? == false
+ end
end
diff --git a/activemodel/test/models/helicopter.rb b/activemodel/test/models/helicopter.rb
index a52b6fb4dd..933f3c463a 100644
--- a/activemodel/test/models/helicopter.rb
+++ b/activemodel/test/models/helicopter.rb
@@ -1,3 +1,7 @@
class Helicopter
include ActiveModel::Conversion
end
+
+class Helicopter::Comanche
+ include ActiveModel::Conversion
+end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index db8fa303be..f1cca0ad76 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,14 +1,44 @@
## Rails 4.0.0 (unreleased) ##
-* Rename `update_attributes` to `update`, keep `update_attributes` as an alias for `update` method.
+* 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*
+
+* Support for PostgreSQL's `ltree` data type.
+
+ *Rob Worley*
+
+* Fix undefined method `to_i` when calling `new` on a scope that uses an
+ Array; Fix FloatDomainError when setting integer column to NaN.
+ Fixes #8718, #8734, #8757.
+
+ *Jason Stirk + Tristan Harward*
+
+* Rename `update_attributes` to `update`, keep `update_attributes` as an alias for `update` method.
This is a soft-deprecation for `update_attributes`, although it will still work without any
deprecation message in 4.0 is recommended to start using `update` since `update_attributes` will be
deprecated and removed in future versions of Rails.
-
+
*Amparo Luna + Guillermo Iguaran*
* `after_commit` and `after_rollback` now validate the `:on` option and raise an `ArgumentError`
- if it is not one of `:create`, `:destroy` or ``:update`
+ if it is not one of `:create`, `:destroy` or `:update`
*Pascal Friederich*
@@ -960,7 +990,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/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..bfc2e54aba 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.2'
end
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index 6acfec02c4..9d1c12ec62 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -165,7 +165,7 @@ module ActiveRecord
# by specifying an instance of the value object in the conditions hash. The following example
# finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
#
- # Customer.where(balance: Money.new(20, "USD")).all
+ # Customer.where(balance: Money.new(20, "USD"))
#
module ClassMethods
# Adds reader and writer methods for manipulating a value object:
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index d8b6d7a86b..16a46a59d1 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1,8 +1,6 @@
require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/string/conversions'
require 'active_support/core_ext/module/remove_method'
-require 'active_support/dependencies/autoload'
-require 'active_support/concern'
require 'active_record/errors'
module ActiveRecord
@@ -194,7 +192,7 @@ module ActiveRecord
# * <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.all(options), Project#milestones.build, Project#milestones.create</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>
#
@@ -459,7 +457,7 @@ module ActiveRecord
# has_many :people do
# def find_or_create_by_name(name)
# first_name, last_name = name.split(" ", 2)
- # find_or_create_by_first_name_and_last_name(first_name, last_name)
+ # find_or_create_by(first_name: first_name, last_name: last_name)
# end
# end
# end
@@ -474,7 +472,7 @@ module ActiveRecord
# module FindOrCreateByNameExtension
# def find_or_create_by_name(name)
# first_name, last_name = name.split(" ", 2)
- # find_or_create_by_first_name_and_last_name(first_name, last_name)
+ # find_or_create_by(first_name: first_name, last_name: last_name)
# end
# end
#
@@ -743,7 +741,7 @@ module ActiveRecord
# other than the main one. If this is the case Active Record falls back to the previously
# used LEFT OUTER JOIN based strategy. For example
#
- # Post.includes([:author, :comments]).where(['comments.approved = ?', true]).all
+ # Post.includes([:author, :comments]).where(['comments.approved = ?', true])
#
# This will result in a single SQL query with joins along the lines of:
# <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
@@ -951,7 +949,7 @@ module ActiveRecord
#
# The <tt>:dependent</tt> option can have different values which specify how the deletion
# is done. For more information, see the documentation for this option on the different
- # specific association types. When no option is given, the behaviour is to do nothing
+ # specific association types. When no option is given, the behavior is to do nothing
# with the associated records when destroying a record.
#
# Note that <tt>:dependent</tt> is implemented using Rails' callback
@@ -1081,7 +1079,7 @@ module ActiveRecord
# === Example
#
# Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
- # * <tt>Firm#clients</tt> (similar to <tt>Clients.all conditions: ["firm_id = ?", id]</tt>)
+ # * <tt>Firm#clients</tt> (similar to <tt>Client.where(firm_id: id)</tt>)
# * <tt>Firm#clients<<</tt>
# * <tt>Firm#clients.delete</tt>
# * <tt>Firm#clients.destroy</tt>
@@ -1091,7 +1089,7 @@ module ActiveRecord
# * <tt>Firm#clients.clear</tt>
# * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
# * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
- # * <tt>Firm#clients.find</tt> (similar to <tt>Client.find(id, conditions: "firm_id = #{id}")</tt>)
+ # * <tt>Firm#clients.find</tt> (similar to <tt>Client.where(firm_id: id).find(id)</tt>)
# * <tt>Firm#clients.exists?(name: 'ACME')</tt> (similar to <tt>Client.exists?(name: 'ACME', firm_id: firm.id)</tt>)
# * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
# * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
@@ -1213,7 +1211,7 @@ module ActiveRecord
# === Example
#
# An Account class declares <tt>has_one :beneficiary</tt>, which will add:
- # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.first(conditions: "account_id = #{id}")</tt>)
+ # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.where(account_id: id).first</tt>)
# * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
# * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
# * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 1303822868..300f67959d 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -16,6 +16,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
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index 75f72c1a46..532af3e83e 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -50,8 +50,11 @@ module ActiveRecord
# Checks whether record is different to the current target, without loading it
def different_target?(record)
- record.nil? && owner[reflection.foreign_key] ||
- record && record.id != owner[reflection.foreign_key]
+ if record.nil?
+ owner[reflection.foreign_key]
+ else
+ record.id != owner[reflection.foreign_key]
+ end
end
def replace_keys(record)
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 7c43e37cf2..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
@@ -758,7 +759,7 @@ module ActiveRecord
# person.pets.count # => 0
# person.pets.any? # => true
#
- # You can also pass a block to define criteria. The behaviour
+ # You can also pass a block to define criteria. The behavior
# is the same, it returns true if the collection based on the
# criteria is not empty.
#
@@ -793,7 +794,7 @@ module ActiveRecord
# person.pets.many? #=> true
#
# You can also pass a block to define criteria. The
- # behaviour is the same, it returns true if the collection
+ # behavior is the same, it returns true if the collection
# based on the criteria has more than one record.
#
# person.pets
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index c3266f2bb4..d1458f30ba 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -114,11 +114,7 @@ module ActiveRecord
end
def target_reflection_has_associated_record?
- if through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank?
- false
- else
- true
- end
+ !(through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank?)
end
def update_through_counter?(method)
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index cbf5e734ea..82588905c6 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -76,7 +76,7 @@ module ActiveRecord
else
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
# Make several smaller queries if necessary or make one query if the adapter supports it
- sliced = owner_keys.each_slice(model.connection.in_clause_length || owner_keys.size)
+ sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
records = sliced.map { |slice| records_for(slice).to_a }.flatten
end
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 7e357aa2f4..616ae1631f 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -1,5 +1,4 @@
require 'active_support/core_ext/module/attribute_accessors'
-require 'active_support/deprecation'
module ActiveRecord
module AttributeMethods
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 0857b02c8e..310f1b6e75 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -76,7 +76,7 @@ module ActiveRecord
end
def get_primary_key(base_name) #:nodoc:
- return 'id' unless base_name && !base_name.blank?
+ return 'id' if base_name.blank?
case primary_key_prefix_type
when :table_name
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index aab832c2f7..bf5793d454 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -162,12 +162,9 @@ 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>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt> and thus produces finders
- # like <tt>Person.find_by_user_name</tt>, <tt>Person.find_all_by_last_name</tt>, and
- # <tt>Payment.find_by_transaction_id</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>.
- # And instead of writing <tt>Person.where(last_name: last_name).all</tt>, you just do
- # <tt>Person.find_all_by_last_name(last_name)</tt>.
+ # 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
# <tt>ActiveRecord::RecordNotFound</tt> error if they do not return any records,
@@ -180,46 +177,7 @@ module ActiveRecord #:nodoc:
#
# It's even possible to call these dynamic finder methods on relations and named scopes.
#
- # Payment.order("created_on").find_all_by_amount(50)
- # Payment.pending.find_last_by_amount(100)
- #
- # The same dynamic finder style can be used to create the object if it doesn't already exist.
- # This dynamic finder is called with <tt>find_or_create_by_</tt> and will return the object if
- # it already exists and otherwise creates it, then returns it. Protected attributes won't be set
- # unless they are given in a block.
- #
- # # No 'Summer' tag exists
- # Tag.find_or_create_by_name("Summer") # equal to Tag.create(name: "Summer")
- #
- # # Now the 'Summer' tag does exist
- # Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer")
- #
- # # Now 'Bob' exist and is an 'admin'
- # User.find_or_create_by_name('Bob', age: 40) { |u| u.admin = true }
- #
- # Adding an exclamation point (!) on to the end of <tt>find_or_create_by_</tt> will
- # raise an <tt>ActiveRecord::RecordInvalid</tt> error if the new record is invalid.
- #
- # Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without
- # saving it first. Protected attributes won't be set unless they are given in a block.
- #
- # # No 'Winter' tag exists
- # winter = Tag.find_or_initialize_by_name("Winter")
- # winter.persisted? # false
- #
- # To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of
- # a list of parameters.
- #
- # Tag.find_or_create_by_name(name: "rails", creator: current_user)
- #
- # That will either find an existing tag named "rails", or create a new one while setting the
- # user that created it.
- #
- # Just like <tt>find_by_*</tt>, you can also use <tt>scoped_by_*</tt> to retrieve data. The good thing about
- # using this feature is that the very first time result is returned using <tt>method_missing</tt> technique
- # but after that the method is declared on the class. Henceforth <tt>method_missing</tt> will not be hit.
- #
- # User.scoped_by_user_name('David')
+ # Payment.order("created_on").find_by_amount(50)
#
# == Saving arrays, hashes, and other non-mappable objects in text columns
#
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 82d0cf7e2e..3675184193 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -2,7 +2,6 @@ require 'thread'
require 'thread_safe'
require 'monitor'
require 'set'
-require 'active_support/deprecation'
module ActiveRecord
# Raised when a connection could not be obtained within the connection
@@ -518,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/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 8517ce5fc5..cbb6869e66 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -5,7 +5,6 @@ require 'active_support/core_ext/benchmark'
require 'active_record/connection_adapters/schema_cache'
require 'active_record/connection_adapters/abstract/schema_dumper'
require 'monitor'
-require 'active_support/deprecation'
module ActiveRecord
module ConnectionAdapters # :nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index fd36a5b075..fb28ecb6cf 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -206,7 +206,7 @@ module ActiveRecord
when TrueClass, FalseClass
value ? 1 : 0
else
- value.to_i
+ value.to_i rescue nil
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index a6013f754a..20a5ca2baa 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -7,13 +7,15 @@ module ActiveRecord
module ConnectionHandling
# Establishes a connection to the database that's used by all Active Record objects.
def mysql2_connection(config)
+ config = config.symbolize_keys
+
config[:username] = 'root' if config[:username].nil?
if Mysql2::Client.const_defined? :FOUND_ROWS
config[:flags] = Mysql2::Client::FOUND_ROWS
end
- client = Mysql2::Client.new(config.symbolize_keys)
+ client = Mysql2::Client.new(config)
options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
index 34d7a246b2..9b5170f657 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -1,5 +1,3 @@
-require 'active_support/deprecation'
-
module ActiveRecord
module ConnectionAdapters
class PostgreSQLAdapter < AbstractAdapter
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
index 18ea83ce42..02c295983f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -82,7 +82,7 @@ module ActiveRecord
def type_cast(value)
return if value.nil?
- value.to_i rescue value ? 1 : 0
+ ConnectionAdapters::Column.value_to_integer value
end
end
@@ -276,6 +276,7 @@ module ActiveRecord
register_type 'circle', OID::Identity.new
register_type 'hstore', OID::Hstore.new
register_type 'json', OID::Json.new
+ register_type 'ltree', OID::Identity.new
register_type 'int4range', OID::IntRange.new
alias_type 'int8range', 'int4range'
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 e10b562fa4..8c68576bdc 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -18,9 +18,9 @@ module ActiveRecord
# create_database config[:database], config
# create_database 'foo_development', encoding: 'unicode'
def create_database(name, options = {})
- options = options.reverse_merge(:encoding => "utf8")
+ options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
- option_string = options.symbolize_keys.sum do |key, value|
+ option_string = options.sum do |key, value|
case key
when :owner
" OWNER = \"#{value}\""
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index d62a375470..b1b0467379 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -17,22 +17,25 @@ require 'ipaddr'
module ActiveRecord
module ConnectionHandling
+ VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
+ :client_encoding, :options, :application_name, :fallback_application_name,
+ :keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
+ :tty, :sslmode, :requiressl, :sslcert, :sslkey, :sslrootcert, :sslcrl,
+ :requirepeer, :krbsrvname, :gsslib, :service]
+
# Establishes a connection to the database that's used by all Active Record objects
def postgresql_connection(config) # :nodoc:
conn_params = config.symbolize_keys
- # Forward any unused config params to PGconn.connect.
- [:statement_limit, :encoding, :min_messages, :schema_search_path,
- :schema_order, :adapter, :pool, :checkout_timeout, :template,
- :reaping_frequency, :insert_returning, :variables].each do |key|
- conn_params.delete key
- end
- conn_params.delete_if { |k,v| v.nil? }
+ conn_params.delete_if { |_, v| v.nil? }
# Map ActiveRecords param names to PGs.
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
+ # Forward only valid config params to PGconn.connect.
+ conn_params.keep_if { |k, _| VALID_CONN_PARAMS.include?(k) }
+
# The postgres drivers don't allow the creation of an unconnected PGconn object,
# so just pass a nil connection object for the time being.
ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
@@ -173,6 +176,8 @@ module ActiveRecord
:decimal
when 'hstore'
:hstore
+ when 'ltree'
+ :ltree
# Network address types
when 'inet'
:inet
@@ -275,6 +280,10 @@ module ActiveRecord
column(name, 'hstore', options)
end
+ def ltree(name, options = {})
+ column(name, 'ltree', options)
+ end
+
def inet(name, options = {})
column(name, 'inet', options)
end
@@ -340,7 +349,8 @@ module ActiveRecord
macaddr: { name: "macaddr" },
uuid: { name: "uuid" },
json: { name: "json" },
- intrange: { name: "int4range" }
+ intrange: { name: "int4range" },
+ ltree: { name: "ltree" }
}
include Quoting
@@ -451,8 +461,6 @@ module ActiveRecord
@visitor = BindSubstitution.new self
end
- connection_parameters.delete :prepared_statements
-
@connection_parameters, @config = connection_parameters, config
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
@@ -640,32 +648,30 @@ module ActiveRecord
end
def exec_cache(sql, binds)
+ stmt_key = prepare_statement sql
+
+ # Clear the queue
+ @connection.get_last_result
+ @connection.send_query_prepared(stmt_key, binds.map { |col, val|
+ type_cast(val, col)
+ })
+ @connection.block
+ @connection.get_last_result
+ rescue PGError => e
+ # Get the PG code for the failure. Annoyingly, the code for
+ # prepared statements whose return value may have changed is
+ # FEATURE_NOT_SUPPORTED. Check here for more details:
+ # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
begin
- stmt_key = prepare_statement sql
-
- # Clear the queue
- @connection.get_last_result
- @connection.send_query_prepared(stmt_key, binds.map { |col, val|
- type_cast(val, col)
- })
- @connection.block
- @connection.get_last_result
- rescue PGError => e
- # Get the PG code for the failure. Annoyingly, the code for
- # prepared statements whose return value may have changed is
- # FEATURE_NOT_SUPPORTED. Check here for more details:
- # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
- begin
- code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
- rescue
- raise e
- end
- if FEATURE_NOT_SUPPORTED == code
- @statements.delete sql_key(sql)
- retry
- else
- raise e
- end
+ code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
+ rescue
+ raise e
+ end
+ if FEATURE_NOT_SUPPORTED == code
+ @statements.delete sql_key(sql)
+ retry
+ else
+ raise e
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 94c6684700..812f1ce5c5 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
- @_start_transaction_state = {}
+ @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 ea3bb8f33f..5a4ef5991d 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -5,8 +5,6 @@ require 'active_support/dependencies'
require 'active_record/fixture_set/file'
require 'active_record/errors'
-require 'active_support/deprecation' # temporary
-
module ActiveRecord
class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
end
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 6ab67fdece..e630897a4b 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/indifferent_access'
+
module ActiveRecord
module Inheritance
extend ActiveSupport::Concern
diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb
index b4bb95a392..8e4ddcac82 100644
--- a/activerecord/lib/active_record/locking/pessimistic.rb
+++ b/activerecord/lib/active_record/locking/pessimistic.rb
@@ -26,7 +26,7 @@ module ActiveRecord
#
# Account.transaction do
# # select * from accounts where ...
- # accounts = Account.where(...).all
+ # accounts = Account.where(...)
# account1 = accounts.detect { |account| ... }
# account2 = accounts.detect { |account| ... }
# # select * from accounts where id=? for update
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index 2366a91bb5..c1ba524c84 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -21,13 +21,15 @@ module ActiveRecord
end
def render_bind(column, value)
- if column.type == :binary
- rendered_value = "<#{value.bytesize} bytes of binary data>"
+ if column
+ if column.binary?
+ value = "<#{value.bytesize} bytes of binary data>"
+ end
+
+ [column.name, value]
else
- rendered_value = value
+ [nil, value]
end
-
- [column.name, rendered_value]
end
def sql(event)
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 3011f959a5..1b2aa9349e 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -236,26 +236,26 @@ module ActiveRecord
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/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 0b27ea730b..3f154bd1cc 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -1,5 +1,3 @@
-require 'active_support/deprecation'
-
module ActiveRecord
module Calculations
# Count the records.
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 431d083f21..615309964c 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -1,4 +1,3 @@
-require 'active_support/concern'
require 'thread'
require 'thread_safe'
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 83074e72c1..883d25d80b 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 = 2'
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
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index 8b7eda6eee..01fbb96b8e 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -134,16 +134,14 @@ module ActiveRecord
# end
#
# def self.titles
- # map(&:title)
+ # pluck(:title)
# end
- #
# end
#
# We are able to call the methods like this:
#
# Article.published.featured.latest_article
# Article.featured.titles
-
def scope(name, body, &block)
extension = Module.new(&block) if block
diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb
index df7f58c81f..cf4cf9e602 100644
--- a/activerecord/lib/active_record/store.rb
+++ b/activerecord/lib/active_record/store.rb
@@ -107,7 +107,7 @@ module ActiveRecord
private
def initialize_store_attribute(store_attribute)
attribute = send(store_attribute)
- unless attribute.is_a?(HashWithIndifferentAccess)
+ unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
attribute = IndifferentCoder.as_indifferent_hash(attribute)
send :"#{store_attribute}=", attribute
end
@@ -134,12 +134,12 @@ module ActiveRecord
def self.as_indifferent_hash(obj)
case obj
- when HashWithIndifferentAccess
+ when ActiveSupport::HashWithIndifferentAccess
obj
when Hash
obj.with_indifferent_access
else
- HashWithIndifferentAccess.new
+ ActiveSupport::HashWithIndifferentAccess.new
end
end
end
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index fda51b3d76..67c7e714e6 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -1,5 +1,7 @@
module ActiveRecord
module Tasks # :nodoc:
+ class DatabaseAlreadyExists < StandardError; end # :nodoc:
+
module DatabaseTasks # :nodoc:
extend self
@@ -32,6 +34,8 @@ module ActiveRecord
def create(*arguments)
configuration = arguments.first
class_for_adapter(configuration['adapter']).new(*arguments).create
+ rescue DatabaseAlreadyExists
+ $stderr.puts "#{configuration['database']} already exists"
rescue Exception => error
$stderr.puts error, *(error.backtrace)
$stderr.puts "Couldn't create database for #{configuration.inspect}"
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index 3d27c97254..17378969a5 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -1,7 +1,6 @@
module ActiveRecord
module Tasks # :nodoc:
class MySQLDatabaseTasks # :nodoc:
-
DEFAULT_CHARSET = ENV['CHARSET'] || 'utf8'
DEFAULT_COLLATION = ENV['COLLATION'] || 'utf8_unicode_ci'
ACCESS_DENIED_ERROR = 1045
@@ -16,18 +15,23 @@ module ActiveRecord
establish_connection configuration_without_database
connection.create_database configuration['database'], creation_options
establish_connection configuration
+ rescue ActiveRecord::StatementInvalid => error
+ if /database exists/ === error.message
+ raise DatabaseAlreadyExists
+ else
+ raise
+ end
rescue error_class => error
- raise error unless error.errno == ACCESS_DENIED_ERROR
-
- $stdout.print error.error
- establish_connection root_configuration_without_database
- connection.create_database configuration['database'], creation_options
- connection.execute grant_statement.gsub(/\s+/, ' ').strip
- establish_connection configuration
- rescue error_class => error
- $stderr.puts error.error
- $stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}"
- $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding']
+ if error.respond_to?(:errno) && error.errno == ACCESS_DENIED_ERROR
+ $stdout.print error.error
+ establish_connection root_configuration_without_database
+ connection.create_database configuration['database'], creation_options
+ connection.execute grant_statement.gsub(/\s+/, ' ').strip
+ establish_connection configuration
+ else
+ $stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}"
+ $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding']
+ end
end
def drop
@@ -87,14 +91,15 @@ module ActiveRecord
end
def error_class
- case configuration['adapter']
- when /jdbc/
+ if configuration['adapter'] =~ /jdbc/
require 'active_record/railties/jdbcmysql_error'
ArJdbcMySQL::Error
- when /mysql2/
+ elsif defined?(Mysql2)
Mysql2::Error
- else
+ elsif defined?(Mysql)
Mysql::Error
+ else
+ StandardError
end
end
@@ -128,7 +133,6 @@ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
end
args
end
-
end
end
end
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index ea5cb888fb..0b1b030516 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -3,7 +3,6 @@ require 'shellwords'
module ActiveRecord
module Tasks # :nodoc:
class PostgreSQLDatabaseTasks # :nodoc:
-
DEFAULT_ENCODING = ENV['CHARSET'] || 'utf8'
delegate :connection, :establish_connection, :clear_active_connections!,
@@ -18,6 +17,12 @@ module ActiveRecord
connection.create_database configuration['database'],
configuration.merge('encoding' => encoding)
establish_connection configuration
+ rescue ActiveRecord::StatementInvalid => error
+ if /database .* already exists/ === error.message
+ raise DatabaseAlreadyExists
+ else
+ raise
+ end
end
def drop
diff --git a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
index da01058a82..de8b16627e 100644
--- a/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/sqlite_database_tasks.rb
@@ -1,7 +1,6 @@
module ActiveRecord
module Tasks # :nodoc:
class SQLiteDatabaseTasks # :nodoc:
-
delegate :connection, :establish_connection, to: ActiveRecord::Base
def initialize(configuration, root = Rails.root)
@@ -9,10 +8,7 @@ module ActiveRecord
end
def create
- if File.exist?(configuration['database'])
- $stderr.puts "#{configuration['database']} already exists"
- return
- end
+ raise DatabaseAlreadyExists if File.exist?(configuration['database'])
establish_connection configuration
connection
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/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 ddfe42b375..0eb1cc511e 100644
--- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
@@ -49,13 +49,11 @@ module ActiveRecord
end
def test_tables_quoting
- begin
- @conn.tables(nil, "foo-bar", nil)
- flunk
- rescue => e
- # assertion for *quoted* database properly
- assert_match(/database 'foo-bar'/, e.inspect)
- end
+ @conn.tables(nil, "foo-bar", nil)
+ flunk
+ rescue => e
+ # assertion for *quoted* database properly
+ assert_match(/database 'foo-bar'/, e.inspect)
end
def test_pk_and_sequence_for
diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb
index 2c0ed73c92..94429e772f 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb
@@ -37,13 +37,11 @@ module ActiveRecord
end
def test_tables_quoting
- begin
- @connection.tables(nil, "foo-bar", nil)
- flunk
- rescue => e
- # assertion for *quoted* database properly
- assert_match(/database 'foo-bar'/, e.inspect)
- end
+ @connection.tables(nil, "foo-bar", nil)
+ flunk
+ rescue => e
+ # assertion for *quoted* database properly
+ assert_match(/database 'foo-bar'/, e.inspect)
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
index 1b4f4a5fc9..01c3e6b49b 100644
--- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
@@ -16,6 +16,7 @@ class PostgresqlActiveSchemaTest < ActiveRecord::TestCase
def test_create_database_with_encoding
assert_equal %(CREATE DATABASE "matt" ENCODING = 'utf8'), create_database(:matt)
assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1)
+ assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, 'encoding' => :latin1)
end
def test_create_database_with_collation_and_ctype
diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
index c7ce43d71e..2254be8612 100644
--- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
@@ -30,6 +30,9 @@ end
class PostgresqlUUID < ActiveRecord::Base
end
+class PostgresqlLtree < ActiveRecord::Base
+end
+
class PostgresqlDataTypeTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false
@@ -37,38 +40,43 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
@connection = ActiveRecord::Base.connection
@connection.execute("set lc_monetary = 'C'")
- @connection.execute("INSERT INTO postgresql_arrays (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )")
+ @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("INSERT INTO postgresql_tsvectors (text_vector) VALUES (' ''text'' ''vector'' ')")
+ @connection.execute("INSERT INTO postgresql_tsvectors (id, text_vector) VALUES (1, ' ''text'' ''vector'' ')")
@first_tsvector = PostgresqlTsvector.find(1)
- @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('567.89'::money)")
- @connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('-567.89'::money)")
+ @connection.execute("INSERT INTO postgresql_moneys (id, wealth) VALUES (1, '567.89'::money)")
+ @connection.execute("INSERT INTO postgresql_moneys (id, wealth) VALUES (2, '-567.89'::money)")
@first_money = PostgresqlMoney.find(1)
@second_money = PostgresqlMoney.find(2)
- @connection.execute("INSERT INTO postgresql_numbers (single, double) VALUES (123.456, 123456.789)")
+ @connection.execute("INSERT INTO postgresql_numbers (id, single, double) VALUES (1, 123.456, 123456.789)")
@first_number = PostgresqlNumber.find(1)
- @connection.execute("INSERT INTO postgresql_times (time_interval, scaled_time_interval) VALUES ('1 year 2 days ago', '3 weeks ago')")
+ @connection.execute("INSERT INTO postgresql_times (id, time_interval, scaled_time_interval) VALUES (1, '1 year 2 days ago', '3 weeks ago')")
@first_time = PostgresqlTime.find(1)
- @connection.execute("INSERT INTO postgresql_network_addresses (cidr_address, inet_address, mac_address) VALUES('192.168.0/24', '172.16.1.254/32', '01:23:45:67:89:0a')")
+ @connection.execute("INSERT INTO postgresql_network_addresses (id, cidr_address, inet_address, mac_address) VALUES(1, '192.168.0/24', '172.16.1.254/32', '01:23:45:67:89:0a')")
@first_network_address = PostgresqlNetworkAddress.find(1)
- @connection.execute("INSERT INTO postgresql_bit_strings (bit_string, bit_string_varying) VALUES (B'00010101', X'15')")
+ @connection.execute("INSERT INTO postgresql_bit_strings (id, bit_string, bit_string_varying) VALUES (1, B'00010101', X'15')")
@first_bit_string = PostgresqlBitString.find(1)
- @connection.execute("INSERT INTO postgresql_oids (obj_id) VALUES (1234)")
+ @connection.execute("INSERT INTO postgresql_oids (id, obj_id) VALUES (1, 1234)")
@first_oid = PostgresqlOid.find(1)
- @connection.execute("INSERT INTO postgresql_timestamp_with_zones (time) VALUES ('2010-01-01 10:00:00-1')")
+ @connection.execute("INSERT INTO postgresql_timestamp_with_zones (id, time) VALUES (1, '2010-01-01 10:00:00-1')")
- @connection.execute("INSERT INTO postgresql_uuids (guid, compact_guid) VALUES('d96c3da0-96c1-012f-1316-64ce8f32c6d8', 'f06c715096c1012f131764ce8f32c6d8')")
+ @connection.execute("INSERT INTO postgresql_uuids (id, guid, compact_guid) VALUES(1, 'd96c3da0-96c1-012f-1316-64ce8f32c6d8', 'f06c715096c1012f131764ce8f32c6d8')")
@first_uuid = PostgresqlUUID.find(1)
end
+ def teardown
+ [PostgresqlArray, PostgresqlTsvector, PostgresqlMoney, PostgresqlNumber, PostgresqlTime, PostgresqlNetworkAddress,
+ PostgresqlBitString, PostgresqlOid, PostgresqlTimestampWithZone, PostgresqlUUID].each(&:delete_all)
+ end
+
def test_data_type_of_array_types
assert_equal :integer, @first_array.column_for_attribute(:commission_by_quarter).type
assert_equal :text, @first_array.column_for_attribute(:nicknames).type
diff --git a/activerecord/test/cases/adapters/postgresql/ltree_test.rb b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
new file mode 100644
index 0000000000..5d12ca75ca
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
@@ -0,0 +1,41 @@
+# encoding: utf-8
+require "cases/helper"
+require 'active_record/base'
+require 'active_record/connection_adapters/postgresql_adapter'
+
+class PostgresqlLtreeTest < ActiveRecord::TestCase
+ class Ltree < ActiveRecord::Base
+ self.table_name = 'ltrees'
+ end
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ @connection.transaction do
+ @connection.create_table('ltrees') do |t|
+ t.ltree 'path'
+ end
+ end
+ rescue ActiveRecord::StatementInvalid
+ skip "do not test on PG without ltree"
+ end
+
+ def teardown
+ @connection.execute 'drop table if exists ltrees'
+ end
+
+ def test_column
+ column = Ltree.columns_hash['path']
+ assert_equal :ltree, column.type
+ end
+
+ def test_write
+ ltree = Ltree.new(path: '1.2.3.4')
+ assert ltree.save!
+ end
+
+ def test_select
+ @connection.execute "insert into ltrees (path) VALUES ('1.2.3')"
+ ltree = Ltree.first
+ assert_equal '1.2.3', ltree.path
+ end
+end
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index b2eac0349b..244e0b7179 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -7,10 +7,12 @@ if ActiveRecord::Base.connection.supports_migrations?
def setup
@connection = ActiveRecord::Base.connection
+ ActiveRecord::SchemaMigration.drop_table
end
def teardown
@connection.drop_table :fruits rescue nil
+ ActiveRecord::SchemaMigration.delete_all rescue nil
end
def test_schema_define
@@ -45,5 +47,4 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_nothing_raised { @connection.select_all "SELECT * FROM fruits" }
end
end
-
end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 97a3d57fe0..3a6da0e59f 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -63,6 +63,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal apple.id, citibank.firm_id
end
+ def test_id_assignment
+ apple = Firm.create("name" => "Apple")
+ citibank = Account.create("credit_limit" => 10)
+ citibank.firm_id = apple
+ assert_nil citibank.firm_id
+ end
+
def test_natural_assignment_with_primary_key
apple = Firm.create("name" => "Apple")
citibank = Client.create("name" => "Primary key client")
@@ -567,6 +574,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal new_firm.name, "Apple"
end
+ def test_attributes_are_set_without_error_when_initialized_from_belongs_to_association_with_array_in_where_clause
+ new_account = Account.where(:credit_limit => [ 50, 60 ]).new
+ assert_nil new_account.credit_limit
+ end
+
def test_reassigning_the_parent_id_updates_the_object
client = companies(:second_client)
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index ce1da53859..be2d30641e 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -93,31 +93,31 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_preloading_has_many_in_multiple_queries_with_more_ids_than_database_can_handle
- Post.connection.expects(:in_clause_length).at_least_once.returns(5)
+ Comment.connection.expects(:in_clause_length).at_least_once.returns(5)
posts = Post.all.merge!(:includes=>:comments).to_a
assert_equal 11, posts.size
end
def test_preloading_has_many_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
- Post.connection.expects(:in_clause_length).at_least_once.returns(nil)
+ Comment.connection.expects(:in_clause_length).at_least_once.returns(nil)
posts = Post.all.merge!(:includes=>:comments).to_a
assert_equal 11, posts.size
end
def test_preloading_habtm_in_multiple_queries_with_more_ids_than_database_can_handle
- Post.connection.expects(:in_clause_length).at_least_once.returns(5)
+ Comment.connection.expects(:in_clause_length).at_least_once.returns(5)
posts = Post.all.merge!(:includes=>:categories).to_a
assert_equal 11, posts.size
end
def test_preloading_habtm_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
- Post.connection.expects(:in_clause_length).at_least_once.returns(nil)
+ Comment.connection.expects(:in_clause_length).at_least_once.returns(nil)
posts = Post.all.merge!(:includes=>:categories).to_a
assert_equal 11, posts.size
end
def test_load_associated_records_in_one_query_when_adapter_has_no_limit
- Post.connection.expects(:in_clause_length).at_least_once.returns(nil)
+ Comment.connection.expects(:in_clause_length).at_least_once.returns(nil)
post = posts(:welcome)
assert_queries(2) do
@@ -126,7 +126,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_load_associated_records_in_several_queries_when_many_ids_passed
- Post.connection.expects(:in_clause_length).at_least_once.returns(1)
+ Comment.connection.expects(:in_clause_length).at_least_once.returns(1)
post1, post2 = posts(:welcome), posts(:thinking)
assert_queries(3) do
@@ -135,7 +135,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_load_associated_records_in_one_query_when_a_few_ids_passed
- Post.connection.expects(:in_clause_length).at_least_once.returns(3)
+ Comment.connection.expects(:in_clause_length).at_least_once.returns(3)
post = posts(:welcome)
assert_queries(2) do
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 7e6c7d5862..d42630e1b7 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -625,6 +625,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 +1712,21 @@ 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
end
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 9b00c21b52..10ec33be75 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -443,8 +443,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_has_many_through_uses_conditions_specified_on_the_has_many_association
author = Author.first
- assert_present author.comments
- assert_blank author.nonexistant_comments
+ assert author.comments.present?
+ assert author.nonexistant_comments.blank?
end
def test_has_many_through_uses_correct_attributes
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/base_test.rb b/activerecord/test/cases/base_test.rb
index 5cf597b9f2..4f46459ab3 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -300,13 +300,11 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_initialize_with_invalid_attribute
- begin
- Topic.new({ "title" => "test",
- "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
- rescue ActiveRecord::MultiparameterAssignmentErrors => ex
- assert_equal(1, ex.errors.size)
- assert_equal("last_read", ex.errors[0].attribute)
- end
+ Topic.new({ "title" => "test",
+ "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
+ rescue ActiveRecord::MultiparameterAssignmentErrors => ex
+ assert_equal(1, ex.errors.size)
+ assert_equal("last_read", ex.errors[0].attribute)
end
def test_create_after_initialize_without_block
diff --git a/activerecord/test/cases/column_test.rb b/activerecord/test/cases/column_test.rb
index 2124c256fa..adbe51f430 100644
--- a/activerecord/test/cases/column_test.rb
+++ b/activerecord/test/cases/column_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require 'models/company'
module ActiveRecord
module ConnectionAdapters
@@ -40,13 +41,26 @@ module ActiveRecord
def test_type_cast_non_integer_to_integer
column = Column.new("field", nil, "integer")
- assert_raises(NoMethodError) do
- column.type_cast([])
- end
+ assert_nil column.type_cast([1,2])
+ assert_nil column.type_cast({1 => 2})
+ assert_nil column.type_cast((1..2))
+ end
- assert_raises(NoMethodError) do
- column.type_cast(Object.new)
- end
+ def test_type_cast_activerecord_to_integer
+ column = Column.new("field", nil, "integer")
+ firm = Firm.create(:name => 'Apple')
+ assert_nil column.type_cast(firm)
+ end
+
+ def test_type_cast_object_without_to_i_to_integer
+ column = Column.new("field", nil, "integer")
+ assert_nil column.type_cast(Object.new)
+ end
+
+ def test_type_cast_nan_and_infinity_to_integer
+ column = Column.new("field", nil, "integer")
+ assert_nil column.type_cast(Float::NAN)
+ assert_nil column.type_cast(1.0/0.0)
end
def test_type_cast_time
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 0718d0886f..ea344e992b 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -327,6 +327,20 @@ 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/deprecated_dynamic_methods_test.rb b/activerecord/test/cases/deprecated_dynamic_methods_test.rb
index dde36e7f72..8e842d8758 100644
--- a/activerecord/test/cases/deprecated_dynamic_methods_test.rb
+++ b/activerecord/test/cases/deprecated_dynamic_methods_test.rb
@@ -1,7 +1,7 @@
# This file should be deleted when activerecord-deprecated_finders is removed as
# a dependency.
#
-# It is kept for now as there is some fairly nuanced behaviour in the dynamic
+# It is kept for now as there is some fairly nuanced behavior in the dynamic
# finders so it is useful to keep this around to guard against regressions if
# we need to change the code.
@@ -568,9 +568,9 @@ class DynamicScopeTest < ActiveRecord::TestCase
end
def test_dynamic_scope_should_create_methods_after_hitting_method_missing
- assert_blank @test_klass.methods.grep(/scoped_by_type/)
+ assert @test_klass.methods.grep(/scoped_by_type/).blank?
@test_klass.scoped_by_type(nil)
- assert_present @test_klass.methods.grep(/scoped_by_type/)
+ assert @test_klass.methods.grep(/scoped_by_type/).present?
end
def test_dynamic_scope_with_less_number_of_arguments
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/explain_test.rb b/activerecord/test/cases/explain_test.rb
index a7e5fdf709..aa2a6d7509 100644
--- a/activerecord/test/cases/explain_test.rb
+++ b/activerecord/test/cases/explain_test.rb
@@ -122,7 +122,7 @@ if ActiveRecord::Base.connection.supports_explain?
base.expects(:collecting_sqls_for_explain).never
base.logger.expects(:warn).never
base.silence_auto_explain do
- with_threshold(0) { Car.all }
+ with_threshold(0) { Car.all.to_a }
end
end
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index 345e83a102..57eac0c175 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -8,6 +8,19 @@ class LogSubscriberTest < ActiveRecord::TestCase
include ActiveSupport::LogSubscriber::TestHelper
include ActiveSupport::Logger::Severity
+ class TestDebugLogSubscriber < ActiveRecord::LogSubscriber
+ attr_reader :debugs
+
+ def initialize
+ @debugs = []
+ super
+ end
+
+ def debug message
+ @debugs << message
+ end
+ end
+
fixtures :posts
def setup
@@ -30,30 +43,27 @@ class LogSubscriberTest < ActiveRecord::TestCase
def test_schema_statements_are_ignored
event = Struct.new(:duration, :payload)
- logger = Class.new(ActiveRecord::LogSubscriber) {
- attr_accessor :debugs
-
- def initialize
- @debugs = []
- super
- end
-
- def debug message
- @debugs << message
- end
- }.new
+ logger = TestDebugLogSubscriber.new
assert_equal 0, logger.debugs.length
- logger.sql(event.new(0, { :sql => 'hi mom!' }))
+ logger.sql(event.new(0, sql: 'hi mom!'))
assert_equal 1, logger.debugs.length
- logger.sql(event.new(0, { :sql => 'hi mom!', :name => 'foo' }))
+ logger.sql(event.new(0, sql: 'hi mom!', name: 'foo'))
assert_equal 2, logger.debugs.length
- logger.sql(event.new(0, { :sql => 'hi mom!', :name => 'SCHEMA' }))
+ logger.sql(event.new(0, sql: 'hi mom!', name: 'SCHEMA'))
assert_equal 2, logger.debugs.length
end
+ def test_ignore_binds_payload_with_nil_column
+ event = Struct.new(:duration, :payload)
+
+ logger = TestDebugLogSubscriber.new
+ logger.sql(event.new(0, sql: 'hi mom!', binds: [[nil, 1]]))
+ assert_equal 1, logger.debugs.length
+ end
+
def test_basic_query_logging
Developer.all.load
wait
@@ -105,7 +115,7 @@ class LogSubscriberTest < ActiveRecord::TestCase
def test_binary_data_is_not_logged
skip if current_adapter?(:Mysql2Adapter)
- Binary.create(:data => 'some binary data')
+ Binary.create(data: 'some binary data')
wait
assert_match(/<16 bytes of binary data>/, @logger.logged(:debug).join)
end
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 9cb64a6a71..fa8dec0e15 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -24,15 +24,19 @@ class MigrationTest < ActiveRecord::TestCase
def setup
super
- %w(reminders people_reminders prefix_reminders_suffix).each do |table|
+ %w(reminders people_reminders prefix_reminders_suffix p_things_s).each do |table|
Reminder.connection.drop_table(table) rescue nil
end
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/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
index e905006570..b5a69c4a92 100644
--- a/activerecord/test/cases/migrator_test.rb
+++ b/activerecord/test/cases/migrator_test.rb
@@ -29,6 +29,7 @@ module ActiveRecord
def teardown
super
ActiveRecord::SchemaMigration.delete_all rescue nil
+ ActiveRecord::Migration.verbose = true
end
def test_migrator_with_duplicate_names
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 9574678e38..94837341fc 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -311,7 +311,7 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
end
def test_should_also_work_with_a_HashWithIndifferentAccess
- @pirate.ship_attributes = HashWithIndifferentAccess.new(:id => @ship.id, :name => 'Davy Jones Gold Dagger')
+ @pirate.ship_attributes = ActiveSupport::HashWithIndifferentAccess.new(:id => @ship.id, :name => 'Davy Jones Gold Dagger')
assert @pirate.ship.persisted?
assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
@@ -593,7 +593,7 @@ module NestedAttributesOnACollectionAssociationTests
end
def test_should_also_work_with_a_HashWithIndifferentAccess
- @pirate.send(association_setter, HashWithIndifferentAccess.new('foo' => HashWithIndifferentAccess.new(:id => @child_1.id, :name => 'Grace OMalley')))
+ @pirate.send(association_setter, ActiveSupport::HashWithIndifferentAccess.new('foo' => ActiveSupport::HashWithIndifferentAccess.new(:id => @child_1.id, :name => 'Grace OMalley')))
@pirate.save
assert_equal 'Grace OMalley', @child_1.reload.name
end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 85b069b593..b936cca875 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -242,7 +242,7 @@ class PersistencesTest < ActiveRecord::TestCase
assert_equal "David", topic2.author_name
end
- def test_update
+ def test_update_object
topic = Topic.new
topic.title = "Another New Topic"
topic.written_on = "2003-12-12 23:23:00"
@@ -646,7 +646,7 @@ class PersistencesTest < ActiveRecord::TestCase
assert !topic.approved?
assert_equal "The First Topic", topic.title
end
-
+
def test_update_attributes
topic = Topic.find(1)
assert !topic.approved?
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index 297e865308..f69a248491 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -82,6 +82,10 @@ module ActiveRecord
assert_equal 0, Post.where(:posts => {}).count
end
+ def test_where_with_table_name_and_empty_array
+ assert_equal 0, Post.where(:id => []).count
+ end
+
def test_where_with_empty_hash_and_no_foreign_key
assert_equal 0, Edge.where(:sink => {}).count
end
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index d318dab1e1..7388324a0d 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
@@ -227,7 +249,7 @@ class NestedRelationScopingTest < ActiveRecord::TestCase
def test_nested_exclusive_scope_for_create
comment = Comment.create_with(:body => "Hey guys, nested scopes are broken. Please fix!").scoping do
Comment.unscoped.create_with(:post_id => 1).scoping do
- assert_blank Comment.new.body
+ assert Comment.new.body.blank?
Comment.create :body => "Hey guys"
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 0cd838c0b0..64f1c9f6cc 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')
@@ -509,7 +516,7 @@ class RelationTest < ActiveRecord::TestCase
def test_find_in_empty_array
authors = Author.all.where(:id => [])
- assert_blank authors.to_a
+ assert authors.to_a.blank?
end
def test_where_with_ar_object
@@ -723,7 +730,7 @@ class RelationTest < ActiveRecord::TestCase
def test_relation_merging_with_locks
devs = Developer.lock.where("salary >= 80000").order("id DESC").merge(Developer.limit(2))
- assert_present devs.locked
+ assert devs.locked.present?
end
def test_relation_merging_with_preload
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 907b7bec30..cae12e0e3a 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -280,6 +280,13 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
end
+ def test_schema_dump_includes_ltrees_shorthand_definition
+ output = standard_dump
+ if %r{create_table "postgresql_ltrees"} =~ output
+ assert_match %r[t.ltree "path"], output
+ end
+ end
+
def test_schema_dump_includes_arrays_shorthand_definition
output = standard_dump
if %r{create_table "postgresql_arrays"} =~ output
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index 562ca8d9ff..43bf285ba9 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -67,9 +67,9 @@ class StoreTest < ActiveRecord::TestCase
end
test "preserve store attributes data in HashWithIndifferentAccess format without any conversion" do
- @john.json_data = HashWithIndifferentAccess.new(:height => 'tall', 'weight' => 'heavy')
+ @john.json_data = ActiveSupport::HashWithIndifferentAccess.new(:height => 'tall', 'weight' => 'heavy')
@john.height = 'low'
- assert_equal true, @john.json_data.instance_of?(HashWithIndifferentAccess)
+ assert_equal true, @john.json_data.instance_of?(ActiveSupport::HashWithIndifferentAccess)
assert_equal 'low', @john.json_data[:height]
assert_equal 'low', @john.json_data['height']
assert_equal 'heavy', @john.json_data[:weight]
@@ -95,7 +95,7 @@ class StoreTest < ActiveRecord::TestCase
test "convert store attributes from any format other than Hash or HashWithIndifferent access losing the data" do
@john.json_data = "somedata"
@john.height = 'low'
- assert_equal true, @john.json_data.instance_of?(HashWithIndifferentAccess)
+ assert_equal true, @john.json_data.instance_of?(ActiveSupport::HashWithIndifferentAccess)
assert_equal 'low', @john.json_data[:height]
assert_equal 'low', @john.json_data['height']
assert_equal false, @john.json_data.delete_if { |k, v| k == 'height' }.any?
@@ -143,5 +143,4 @@ class StoreTest < ActiveRecord::TestCase
assert_raise(NoMethodError) { @john.stored_attributes = Hash.new }
assert_raise(NoMethodError) { @john.stored_attributes }
end
-
end
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index 4f3489b7a5..659d5eae72 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -11,10 +11,10 @@ module ActiveRecord
end
ADAPTERS_TASKS = {
- :mysql => :mysql_tasks,
- :mysql2 => :mysql_tasks,
- :postgresql => :postgresql_tasks,
- :sqlite3 => :sqlite_tasks
+ mysql: :mysql_tasks,
+ mysql2: :mysql_tasks,
+ postgresql: :postgresql_tasks,
+ sqlite3: :sqlite_tasks
}
class DatabaseTasksRegisterTask < ActiveRecord::TestCase
@@ -32,7 +32,7 @@ module ActiveRecord
ActiveRecord::Tasks::DatabaseTasks.structure_dump({'adapter' => :foo}, "awesome-file.sql")
end
end
-
+
class DatabaseTasksCreateTest < ActiveRecord::TestCase
include DatabaseTasksSetupper
@@ -258,7 +258,7 @@ module ActiveRecord
class DatabaseTasksCharsetTest < ActiveRecord::TestCase
include DatabaseTasksSetupper
-
+
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_charset") do
eval("@#{v}").expects(:charset)
@@ -269,7 +269,7 @@ module ActiveRecord
class DatabaseTasksCollationTest < ActiveRecord::TestCase
include DatabaseTasksSetupper
-
+
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_collation") do
eval("@#{v}").expects(:collation)
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index 69a049fcfa..38b9dd02f0 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -53,6 +53,16 @@ module ActiveRecord
ActiveRecord::Tasks::DatabaseTasks.create @configuration
end
+
+ def test_create_when_database_exists_outputs_info_to_stderr
+ $stderr.expects(:puts).with("my-app-db already exists").once
+
+ ActiveRecord::Base.connection.stubs(:create_database).raises(
+ ActiveRecord::StatementInvalid.new("Can't create database 'dev'; database exists:")
+ )
+
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
end
class MysqlDBCreateAsRootTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index 62acd53003..3006a87589 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -61,6 +61,16 @@ module ActiveRecord
ActiveRecord::Tasks::DatabaseTasks.create @configuration
end
+
+ def test_create_when_database_exists_outputs_info_to_stderr
+ $stderr.expects(:puts).with("my-app-db already exists").once
+
+ ActiveRecord::Base.connection.stubs(:create_database).raises(
+ ActiveRecord::StatementInvalid.new('database "my-app-db" already exists')
+ )
+
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
end
class PostgreSQLDBDropTest < ActiveRecord::TestCase
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/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/post.rb b/activerecord/test/models/post.rb
index c995f59a15..603f1f2555 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -5,6 +5,12 @@ 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") }
@@ -29,6 +35,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 +52,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
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 96fef3a831..ae13f2cd8a 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -1,6 +1,6 @@
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
+ %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|
execute "DROP TABLE IF EXISTS #{quote_table_name table_name}"
end
@@ -89,6 +89,15 @@ _SQL
_SQL
end
+ if 't' == select_value("select 'ltree'=ANY(select typname from pg_type)")
+ execute <<_SQL
+ CREATE TABLE postgresql_ltrees (
+ id SERIAL PRIMARY KEY,
+ path ltree
+ );
+_SQL
+ end
+
if 't' == select_value("select 'json'=ANY(select typname from pg_type)")
execute <<_SQL
CREATE TABLE postgresql_json_data_type (
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 6917be7efc..72f28aefc7 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,50 @@
## Rails 4.0.0 (unreleased) ##
+* 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.
+
+ gem 'rails-perftest'
+
+ *Yves Senn*
+
+* Hash.from_xml raises when it encounters type="symbol" or type="yaml".
+ Use Hash.from_trusted_xml to parse this XML.
+
+ CVE-2013-0156
+
+ *Jeremy Kemper*
+
+* Deprecate `assert_present` and `assert_blank` in favor of
+ `assert object.blank?` and `assert object.present?`
+
+ *Yves Senn*
+
+* Change `String#to_date` to use `Date.parse`. This gives more consistent error
+ messages and allows the use of partial dates.
+
+ "gibberish".to_date => Argument Error: invalid date
+ "3rd Feb".to_date => Sun, 03 Feb 2013
+
+ *Kelly Stannard*
+
+* It's now possible to compare `Date`, `DateTime`, `Time` and `TimeWithZone`
+ with `Infinity`. This allows to create date/time ranges with one infinite bound.
+ Example:
+
+ range = Range.new(Date.today, Float::INFINITY)
+
+ Also it's possible to check inclusion of date/time in range with conversion.
+
+ range.include?(Time.now + 1.year) # => true
+ range.include?(DateTime.now + 1.year) # => true
+
+ *Alexander Grebennik*
+
* Remove meaningless `ActiveSupport::FrozenObjectError`, which was just an alias of `RuntimeError`.
*Akira Matsuda*
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index f1aff8a8e3..4b41e6247d 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -72,6 +72,9 @@ module ActiveSupport
@silencers = []
end
+ # Removes all filters, but leaves in silencers. Useful if you suddenly
+ # need to see entire filepaths in the backtrace that you had already
+ # filtered out.
def remove_filters!
@filters = []
end
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/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb
index a8f9dddae5..4f1e432b61 100644
--- a/activesupport/lib/active_support/core_ext/array/access.rb
+++ b/activesupport/lib/active_support/core_ext/array/access.rb
@@ -21,28 +21,28 @@ class Array
# Equal to <tt>self[1]</tt>.
#
- # %w( a b c d e).second # => "b"
+ # %w( a b c d e ).second # => "b"
def second
self[1]
end
# Equal to <tt>self[2]</tt>.
#
- # %w( a b c d e).third # => "c"
+ # %w( a b c d e ).third # => "c"
def third
self[2]
end
# Equal to <tt>self[3]</tt>.
#
- # %w( a b c d e).fourth # => "d"
+ # %w( a b c d e ).fourth # => "d"
def fourth
self[3]
end
# Equal to <tt>self[4]</tt>.
#
- # %w( a b c d e).fifth # => "e"
+ # %w( a b c d e ).fifth # => "e"
def fifth
self[4]
end
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 64e9945ef5..430a35fbaf 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -8,7 +8,7 @@ class Array
# Converts the array to a comma-separated sentence where the last element is
# joined by the connector word.
#
- # You can pass the following options to change the default behaviour. If you
+ # You can pass the following options to change the default behavior. If you
# pass an option key that doesn't exist in the list below, it will raise an
# <tt>ArgumentError</tt>.
#
diff --git a/activesupport/lib/active_support/core_ext/date.rb b/activesupport/lib/active_support/core_ext/date.rb
index 465fedda80..5f13f5f70f 100644
--- a/activesupport/lib/active_support/core_ext/date.rb
+++ b/activesupport/lib/active_support/core_ext/date.rb
@@ -2,4 +2,5 @@ require 'active_support/core_ext/date/acts_like'
require 'active_support/core_ext/date/calculations'
require 'active_support/core_ext/date/conversions'
require 'active_support/core_ext/date/zones'
+require 'active_support/core_ext/date/infinite_comparable'
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 421aa12100..106a65610c 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -8,8 +8,6 @@ require 'active_support/core_ext/date_and_time/calculations'
class Date
include DateAndTime::Calculations
- @beginning_of_week_default = nil
-
class << self
attr_accessor :beginning_of_week_default
diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb
index fe08ade7e0..cdf606f28c 100644
--- a/activesupport/lib/active_support/core_ext/date/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date/conversions.rb
@@ -17,10 +17,10 @@ class Date
}
# Ruby 1.9 has Date#to_time which converts to localtime only.
- remove_possible_method :to_time
+ remove_method :to_time
# Ruby 1.9 has Date#xmlschema which converts to a string without the time component.
- remove_possible_method :xmlschema
+ remove_method :xmlschema
# Convert to a formatted string. See DATE_FORMATS for predefined formats.
#
diff --git a/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb
new file mode 100644
index 0000000000..ca5d793942
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/date/infinite_comparable.rb
@@ -0,0 +1,5 @@
+require 'active_support/core_ext/infinite_comparable'
+
+class Date
+ include InfiniteComparable
+end
diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb
index e8a27b9f38..024af91738 100644
--- a/activesupport/lib/active_support/core_ext/date_time.rb
+++ b/activesupport/lib/active_support/core_ext/date_time.rb
@@ -2,3 +2,4 @@ require 'active_support/core_ext/date_time/acts_like'
require 'active_support/core_ext/date_time/calculations'
require 'active_support/core_ext/date_time/conversions'
require 'active_support/core_ext/date_time/zones'
+require 'active_support/core_ext/date_time/infinite_comparable'
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 fca5d4d679..4e4852a5e6 100644
--- a/activesupport/lib/active_support/core_ext/date_time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date_time/calculations.rb
@@ -142,11 +142,4 @@ class DateTime
def utc_offset
(offset * 86400).to_i
end
-
- # Layers additional behavior on DateTime#<=> so that Time and
- # ActiveSupport::TimeWithZone instances can be compared with a DateTime.
- def <=>(other)
- super other.to_datetime
- end
-
end
diff --git a/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb
new file mode 100644
index 0000000000..8a282b19f2
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/date_time/infinite_comparable.rb
@@ -0,0 +1,5 @@
+require 'active_support/core_ext/infinite_comparable'
+
+class DateTime
+ include InfiniteComparable
+end
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index 6cb7434e5f..8930376ac8 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -101,17 +101,33 @@ class Hash
#
# hash = Hash.from_xml(xml)
# # => {"hash"=>{"foo"=>1, "bar"=>2}}
- def from_xml(xml)
- ActiveSupport::XMLConverter.new(xml).to_h
+ #
+ # DisallowedType is raise if the XML contains attributes with <tt>type="yaml"</tt> or
+ # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to parse this XML.
+ def from_xml(xml, disallowed_types = nil)
+ ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
end
+ # Builds a Hash from XML just like <tt>Hash.from_xml</tt>, but also allows Symbol and YAML.
+ def from_trusted_xml(xml)
+ from_xml xml, []
+ end
end
end
module ActiveSupport
class XMLConverter # :nodoc:
- def initialize(xml)
+ class DisallowedType < StandardError
+ def initialize(type)
+ super "Disallowed type attribute: #{type.inspect}"
+ end
+ end
+
+ DISALLOWED_TYPES = %w(symbol yaml)
+
+ def initialize(xml, disallowed_types = nil)
@xml = normalize_keys(XmlMini.parse(xml))
+ @disallowed_types = disallowed_types || DISALLOWED_TYPES
end
def to_h
@@ -119,7 +135,6 @@ module ActiveSupport
end
private
-
def normalize_keys(params)
case params
when Hash
@@ -145,6 +160,10 @@ module ActiveSupport
end
def process_hash(value)
+ if value.include?('type') && !value['type'].is_a?(Hash) && @disallowed_types.include?(value['type'])
+ raise DisallowedType, value['type']
+ end
+
if become_array?(value)
_, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) })
if entries.nil? || value['__content__'].try(:empty?)
diff --git a/activesupport/lib/active_support/core_ext/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/infinite_comparable.rb
new file mode 100644
index 0000000000..b78b2deaad
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/infinite_comparable.rb
@@ -0,0 +1,35 @@
+require 'active_support/concern'
+require 'active_support/core_ext/module/aliasing'
+require 'active_support/core_ext/object/try'
+
+module InfiniteComparable
+ extend ActiveSupport::Concern
+
+ included do
+ alias_method_chain :<=>, :infinity
+ end
+
+ define_method :'<=>_with_infinity' do |other|
+ if other.class == self.class
+ public_send :'<=>_without_infinity', other
+ else
+ infinite = try(:infinite?)
+ other_infinite = other.try(:infinite?)
+
+ # inf <=> inf
+ if infinite && other_infinite
+ infinite <=> other_infinite
+ # not_inf <=> inf
+ elsif other_infinite
+ -other_infinite
+ # inf <=> not_inf
+ elsif infinite
+ infinite
+ else
+ conversion = "to_#{self.class.name.downcase}"
+ other = other.public_send(conversion) if other.respond_to?(conversion)
+ public_send :'<=>_without_infinity', other
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
index 7b518821c8..79d3303b41 100644
--- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb
+++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb
@@ -59,10 +59,9 @@ module Kernel
#
# puts 'This code gets executed and nothing related to ZeroDivisionError was seen'
def suppress(*exception_classes)
- begin yield
- rescue Exception => e
- raise unless exception_classes.any? { |cls| e.kind_of?(cls) }
- end
+ yield
+ rescue Exception => e
+ raise unless exception_classes.any? { |cls| e.kind_of?(cls) }
end
# Captures the given stream and returns it:
diff --git a/activesupport/lib/active_support/core_ext/marshal.rb b/activesupport/lib/active_support/core_ext/marshal.rb
index fec3051c0c..c7a8348b1d 100644
--- a/activesupport/lib/active_support/core_ext/marshal.rb
+++ b/activesupport/lib/active_support/core_ext/marshal.rb
@@ -1,21 +1,19 @@
module Marshal
class << self
def load_with_autoloading(source)
- begin
- load_without_autoloading(source)
- rescue ArgumentError, NameError => exc
- if exc.message.match(%r|undefined class/module (.+)|)
- # try loading the class/module
- $1.constantize
- # if it is a IO we need to go back to read the object
- source.rewind if source.respond_to?(:rewind)
- retry
- else
- raise exc
- end
+ load_without_autoloading(source)
+ rescue ArgumentError, NameError => exc
+ if exc.message.match(%r|undefined class/module (.+)|)
+ # try loading the class/module
+ $1.constantize
+ # if it is a IO we need to go back to read the object
+ source.rewind if source.respond_to?(:rewind)
+ retry
+ else
+ raise exc
end
end
alias_method_chain :load, :autoloading
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/core_ext/numeric.rb b/activesupport/lib/active_support/core_ext/numeric.rb
index a6bc0624be..d5cfc2ece4 100644
--- a/activesupport/lib/active_support/core_ext/numeric.rb
+++ b/activesupport/lib/active_support/core_ext/numeric.rb
@@ -1,3 +1,4 @@
require 'active_support/core_ext/numeric/bytes'
require 'active_support/core_ext/numeric/time'
require 'active_support/core_ext/numeric/conversions'
+require 'active_support/core_ext/numeric/infinite_comparable'
diff --git a/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb
new file mode 100644
index 0000000000..b5f1b0487b
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/numeric/infinite_comparable.rb
@@ -0,0 +1,9 @@
+require 'active_support/core_ext/infinite_comparable'
+
+class Float
+ include InfiniteComparable
+end
+
+class BigDecimal
+ include InfiniteComparable
+end
diff --git a/activesupport/lib/active_support/core_ext/object/acts_like.rb b/activesupport/lib/active_support/core_ext/object/acts_like.rb
index fcc8e50f06..3912cc5ace 100644
--- a/activesupport/lib/active_support/core_ext/object/acts_like.rb
+++ b/activesupport/lib/active_support/core_ext/object/acts_like.rb
@@ -1,9 +1,9 @@
class Object
# A duck-type assistant method. For example, Active Support extends Date
- # to define an acts_like_date? method, and extends Time to define
- # acts_like_time?. As a result, we can do "x.acts_like?(:time)" and
- # "x.acts_like?(:date)" to do duck-type-safe comparisons, since classes that
- # we want to act like Time simply need to define an acts_like_time? method.
+ # to define an <tt>acts_like_date?</tt> method, and extends Time to define
+ # <tt>acts_like_time?</tt>. As a result, we can do <tt>x.acts_like?(:time)</tt> and
+ # <tt>x.acts_like?(:date)</tt> to do duck-type-safe comparisons, since classes that
+ # we want to act like Time simply need to define an <tt>acts_like_time?</tt> method.
def acts_like?(duck)
respond_to? :"acts_like_#{duck}?"
end
diff --git a/activesupport/lib/active_support/core_ext/object/instance_variables.rb b/activesupport/lib/active_support/core_ext/object/instance_variables.rb
index 40821fd619..755e1c6b16 100644
--- a/activesupport/lib/active_support/core_ext/object/instance_variables.rb
+++ b/activesupport/lib/active_support/core_ext/object/instance_variables.rb
@@ -13,7 +13,7 @@ class Object
Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
end
- # Returns an array of instance variable names including "@".
+ # Returns an array of instance variable names as strings including "@".
#
# class C
# def initialize(x, y)
diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index 9d3b81cf38..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.
@@ -32,11 +42,7 @@ class String
# "2012-12-13".to_date #=> Thu, 13 Dec 2012
# "12/13/2012".to_date #=> ArgumentError: invalid date
def to_date
- unless blank?
- date_values = ::Date._parse(self, false).values_at(:year, :mon, :mday)
-
- ::Date.new(*date_values)
- end
+ ::Date.parse(self, false) unless blank?
end
# Converts a string to a DateTime value.
@@ -46,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/time.rb b/activesupport/lib/active_support/core_ext/time.rb
index 32cffe237d..af6b589b71 100644
--- a/activesupport/lib/active_support/core_ext/time.rb
+++ b/activesupport/lib/active_support/core_ext/time.rb
@@ -3,3 +3,4 @@ require 'active_support/core_ext/time/calculations'
require 'active_support/core_ext/time/conversions'
require 'active_support/core_ext/time/marshal'
require 'active_support/core_ext/time/zones'
+require 'active_support/core_ext/time/infinite_comparable'
diff --git a/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb b/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb
new file mode 100644
index 0000000000..63795885f5
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/time/infinite_comparable.rb
@@ -0,0 +1,5 @@
+require 'active_support/core_ext/infinite_comparable'
+
+class Time
+ include InfiniteComparable
+end
diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb
index 796c5f9805..139d48f59c 100644
--- a/activesupport/lib/active_support/core_ext/time/zones.rb
+++ b/activesupport/lib/active_support/core_ext/time/zones.rb
@@ -1,8 +1,6 @@
require 'active_support/time_with_zone'
class Time
- @zone_default = nil
-
class << self
attr_accessor :zone_default
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/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 1eb2b4212b..39648727fd 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -266,14 +266,12 @@ module ActiveSupport
# 'UnknownModule'.safe_constantize # => nil
# 'UnknownModule::Foo::Bar'.safe_constantize # => nil
def safe_constantize(camel_cased_word)
- begin
- constantize(camel_cased_word)
- rescue NameError => e
- raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
- e.name.to_s == camel_cased_word.to_s
- rescue ArgumentError => e
- raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
- end
+ constantize(camel_cased_word)
+ rescue NameError => e
+ raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
+ e.name.to_s == camel_cased_word.to_s
+ rescue ArgumentError => e
+ raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
end
# Returns the suffix that should be added to a number to denote the position
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index 832d1ce6d5..9bf1ea35b3 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -235,7 +235,7 @@ class BigDecimal
# real value.
#
# Use <tt>ActiveSupport.use_standard_json_big_decimal_format = true</tt> to
- # override this behaviour.
+ # override this behavior.
def as_json(options = nil) #:nodoc:
if finite?
ActiveSupport.encode_big_decimal_as_string ? to_s : self
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/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb
index a58afc6b9d..21a04a9152 100644
--- a/activesupport/lib/active_support/log_subscriber.rb
+++ b/activesupport/lib/active_support/log_subscriber.rb
@@ -53,7 +53,9 @@ module ActiveSupport
class << self
def logger
- @logger ||= Rails.logger if defined?(Rails)
+ if defined?(Rails) && Rails.respond_to?(:logger)
+ @logger ||= Rails.logger
+ end
@logger
end
diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb
index 63dad7e01a..f9a98686d3 100644
--- a/activesupport/lib/active_support/log_subscriber/test_helper.rb
+++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb
@@ -15,7 +15,7 @@ module ActiveSupport
# end
#
# def test_basic_query_logging
- # Developer.all
+ # Developer.all.to_a
# wait
# assert_equal 1, @logger.logged(:debug).size
# assert_match(/Developer Load/, @logger.logged(:debug).last)
diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb
index ab0b162ee0..1ee7ca06bb 100644
--- a/activesupport/lib/active_support/notifications/instrumenter.rb
+++ b/activesupport/lib/active_support/notifications/instrumenter.rb
@@ -7,7 +7,7 @@ module ActiveSupport
attr_reader :id
def initialize(notifier)
- @id = unique_id
+ @id = unique_id
@notifier = notifier
end
@@ -15,21 +15,32 @@ module ActiveSupport
# and publish it. Notice that events get sent even if an error occurs
# in the passed-in block.
def instrument(name, payload={})
- @notifier.start(name, @id, payload)
+ start name, payload
begin
yield
rescue Exception => e
payload[:exception] = [e.class.name, e.message]
raise e
ensure
- @notifier.finish(name, @id, payload)
+ finish name, payload
end
end
+ # Send a start notification with +name+ and +payload+.
+ def start(name, payload)
+ @notifier.start name, @id, payload
+ end
+
+ # Send a finish notification with +name+ and +payload+.
+ def finish(name, payload)
+ @notifier.finish name, @id, payload
+ end
+
private
- def unique_id
- SecureRandom.hex(10)
- end
+
+ def unique_id
+ SecureRandom.hex(10)
+ end
end
class Event
diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb
index 133aa6a054..72ac597d99 100644
--- a/activesupport/lib/active_support/railtie.rb
+++ b/activesupport/lib/active_support/railtie.rb
@@ -13,20 +13,6 @@ 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|
@@ -42,5 +28,21 @@ 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/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb
index 88aebba5c5..175f7ffe5a 100644
--- a/activesupport/lib/active_support/testing/assertions.rb
+++ b/activesupport/lib/active_support/testing/assertions.rb
@@ -103,6 +103,7 @@ module ActiveSupport
#
# assert_blank [], 'this should be blank'
def assert_blank(object, message=nil)
+ ActiveSupport::Deprecation.warn('"assert_blank" is deprecated. Please use "assert object.blank?" instead')
message ||= "#{object.inspect} is not blank"
assert object.blank?, message
end
@@ -117,6 +118,7 @@ module ActiveSupport
#
# assert_present({ data: 'x' }, 'this should not be blank')
def assert_present(object, message=nil)
+ ActiveSupport::Deprecation.warn('"assert_present" is deprecated. Please use "assert object.present?" instead')
message ||= "#{object.inspect} is blank"
assert object.present?, message
end
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index 27d444fd91..e4f8959e7a 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -12,8 +12,8 @@ module ActiveSupport
end
class ProxyTestResult
- def initialize
- @calls = []
+ def initialize(calls = [])
+ @calls = calls
end
def add_error(e)
@@ -27,6 +27,14 @@ module ActiveSupport
end
end
+ def marshal_dump
+ @calls
+ end
+
+ def marshal_load(calls)
+ initialize(calls)
+ end
+
def method_missing(name, *args)
@calls << [name, args]
end
@@ -35,32 +43,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
@@ -76,10 +88,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/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb
deleted file mode 100644
index 7102ffe2ed..0000000000
--- a/activesupport/lib/active_support/testing/performance.rb
+++ /dev/null
@@ -1,271 +0,0 @@
-require 'fileutils'
-require 'active_support/concern'
-require 'active_support/core_ext/class/delegating_attributes'
-require 'active_support/core_ext/string/inflections'
-require 'active_support/core_ext/module/delegation'
-require 'active_support/number_helper'
-
-module ActiveSupport
- module Testing
- module Performance
- extend ActiveSupport::Concern
-
- included do
- superclass_delegating_accessor :profile_options
- self.profile_options = {}
- end
-
- # each implementation should define metrics and freeze the defaults
- DEFAULTS =
- if ARGV.include?('--benchmark') # HAX for rake test
- { :runs => 4,
- :output => 'tmp/performance',
- :benchmark => true }
- else
- { :runs => 1,
- :output => 'tmp/performance',
- :benchmark => false }
- end
-
- def full_profile_options
- DEFAULTS.merge(profile_options)
- end
-
- def full_test_name
- "#{self.class.name}##{method_name}"
- end
-
- def run(runner)
- @runner = runner
-
- run_warmup
- if full_profile_options && metrics = full_profile_options[:metrics]
- metrics.each do |metric_name|
- if klass = Metrics[metric_name.to_sym]
- run_profile(klass.new)
- end
- end
- end
-
- return
- end
-
- def run_test(metric, mode)
- result = '.'
- begin
- run_callbacks :setup
- setup
- metric.send(mode) { __send__ method_name }
- rescue Exception => e
- result = @runner.puke(self.class, method_name, e)
- ensure
- begin
- teardown
- run_callbacks :teardown
- rescue Exception => e
- result = @runner.puke(self.class, method_name, e)
- end
- end
- result
- end
-
- protected
- # overridden by each implementation.
- def run_gc; end
-
- def run_warmup
- run_gc
-
- time = Metrics::Time.new
- run_test(time, :benchmark)
- puts "%s (%s warmup)" % [full_test_name, time.format(time.total)]
-
- run_gc
- end
-
- def run_profile(metric)
- klass = full_profile_options[:benchmark] ? Benchmarker : Profiler
- performer = klass.new(self, metric)
-
- performer.run
- puts performer.report
- performer.record
- end
-
- class Performer
- delegate :run_test, :full_profile_options, :full_test_name, :to => :@harness
-
- def initialize(harness, metric)
- @harness, @metric, @supported = harness, metric, false
- end
-
- def report
- if @supported
- rate = @total / full_profile_options[:runs]
- '%20s: %s' % [@metric.name, @metric.format(rate)]
- else
- '%20s: unsupported' % @metric.name
- end
- end
-
- protected
- def output_filename
- "#{full_profile_options[:output]}/#{full_test_name}_#{@metric.name}"
- end
- end
-
- # overridden by each implementation.
- class Profiler < Performer
- def time_with_block
- before = Time.now
- yield
- Time.now - before
- end
-
- def run; end
- def record; end
- end
-
- class Benchmarker < Performer
- def initialize(*args)
- super
- @supported = @metric.respond_to?('measure')
- end
-
- def run
- return unless @supported
-
- full_profile_options[:runs].to_i.times { run_test(@metric, :benchmark) }
- @total = @metric.total
- end
-
- def record
- avg = @metric.total / full_profile_options[:runs].to_i
- now = Time.now.utc.xmlschema
- with_output_file do |file|
- file.puts "#{avg},#{now},#{environment}"
- end
- end
-
- def environment
- @env ||= [].tap do |env|
- env << "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/
- env << rails_version if defined?(Rails::VERSION::STRING)
- env << "#{RUBY_ENGINE}-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
- env << RUBY_PLATFORM
- end.join(',')
- end
-
- protected
- if defined?(Rails::VERSION::STRING)
- HEADER = 'measurement,created_at,app,rails,ruby,platform'
- else
- HEADER = 'measurement,created_at,app,ruby,platform'
- end
-
- def with_output_file
- fname = output_filename
-
- if new = !File.exist?(fname)
- FileUtils.mkdir_p(File.dirname(fname))
- end
-
- File.open(fname, 'ab') do |file|
- file.puts(HEADER) if new
- yield file
- end
- end
-
- def output_filename
- "#{super}.csv"
- end
-
- def rails_version
- "rails-#{Rails::VERSION::STRING}#{rails_branch}"
- end
-
- def rails_branch
- if File.directory?('vendor/rails/.git')
- Dir.chdir('vendor/rails') do
- ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/
- end
- end
- end
- end
-
- module Metrics
- def self.[](name)
- const_get(name.to_s.camelize)
- rescue NameError
- nil
- end
-
- class Base
- include ActiveSupport::NumberHelper
-
- attr_reader :total
-
- def initialize
- @total = 0
- end
-
- def name
- @name ||= self.class.name.demodulize.underscore
- end
-
- def benchmark
- with_gc_stats do
- before = measure
- yield
- @total += (measure - before)
- end
- end
-
- # overridden by each implementation.
- def profile; end
-
- protected
- # overridden by each implementation.
- def with_gc_stats; end
- end
-
- class Time < Base
- def measure
- ::Time.now.to_f
- end
-
- def format(measurement)
- if measurement < 1
- '%d ms' % (measurement * 1000)
- else
- '%.2f sec' % measurement
- end
- end
- end
-
- class Amount < Base
- def format(measurement)
- number_to_delimited(measurement.floor)
- end
- end
-
- class DigitalInformationUnit < Base
- def format(measurement)
- number_to_human_size(measurement, :precision => 2)
- end
- end
-
- # each implementation provides its own metrics like ProcessTime, Memory or GcRuns
- end
- end
- end
-end
-
-case RUBY_ENGINE
- when 'ruby' then require 'active_support/testing/performance/ruby'
- when 'rbx' then require 'active_support/testing/performance/rubinius'
- when 'jruby' then require 'active_support/testing/performance/jruby'
- else
- $stderr.puts 'Your ruby interpreter is not supported for benchmarking.'
- exit
-end
diff --git a/activesupport/lib/active_support/testing/performance/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb
deleted file mode 100644
index 34e3f9f45f..0000000000
--- a/activesupport/lib/active_support/testing/performance/jruby.rb
+++ /dev/null
@@ -1,115 +0,0 @@
-require 'jruby/profiler'
-require 'java'
-java_import java.lang.management.ManagementFactory
-
-module ActiveSupport
- module Testing
- module Performance
- DEFAULTS.merge!(
- if ARGV.include?('--benchmark')
- {:metrics => [:wall_time, :user_time, :memory, :gc_runs, :gc_time]}
- else
- { :metrics => [:wall_time],
- :formats => [:flat, :graph] }
- end).freeze
-
- protected
- def run_gc
- ManagementFactory.memory_mx_bean.gc
- end
-
- class Profiler < Performer
- def initialize(*args)
- super
- @supported = @metric.is_a?(Metrics::WallTime)
- end
-
- def run
- return unless @supported
-
- @total = time_with_block do
- @data = JRuby::Profiler.profile do
- full_profile_options[:runs].to_i.times { run_test(@metric, :profile) }
- end
- end
- end
-
- def record
- return unless @supported
-
- klasses = full_profile_options[:formats].map { |f| JRuby::Profiler.const_get("#{f.to_s.camelize}ProfilePrinter") }.compact
-
- klasses.each do |klass|
- fname = output_filename(klass)
- FileUtils.mkdir_p(File.dirname(fname))
- File.open(fname, 'wb') do |file|
- klass.new(@data).printProfile(file)
- end
- end
- end
-
- protected
- def output_filename(printer_class)
- suffix =
- case printer_class.name.demodulize
- when 'FlatProfilePrinter'; 'flat.txt'
- when 'GraphProfilePrinter'; 'graph.txt'
- else printer_class.name.sub(/ProfilePrinter$/, '').underscore
- end
-
- "#{super()}_#{suffix}"
- end
- end
-
- module Metrics
- class Base
- def profile
- yield
- end
-
- protected
- def with_gc_stats
- ManagementFactory.memory_mx_bean.gc
- yield
- end
- end
-
- class WallTime < Time
- def measure
- super
- end
- end
-
- class CpuTime < Time
- def measure
- ManagementFactory.thread_mx_bean.get_current_thread_cpu_time / 1000 / 1000 / 1000.0 # seconds
- end
- end
-
- class UserTime < Time
- def measure
- ManagementFactory.thread_mx_bean.get_current_thread_user_time / 1000 / 1000 / 1000.0 # seconds
- end
- end
-
- class Memory < DigitalInformationUnit
- def measure
- ManagementFactory.memory_mx_bean.non_heap_memory_usage.used + ManagementFactory.memory_mx_bean.heap_memory_usage.used
- end
- end
-
- class GcRuns < Amount
- def measure
- ManagementFactory.garbage_collector_mx_beans.inject(0) { |total_runs, current_gc| total_runs += current_gc.collection_count }
- end
- end
-
- class GcTime < Time
- def measure
- ManagementFactory.garbage_collector_mx_beans.inject(0) { |total_time, current_gc| total_time += current_gc.collection_time } / 1000.0 # seconds
- end
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb
deleted file mode 100644
index d9ebfbe352..0000000000
--- a/activesupport/lib/active_support/testing/performance/rubinius.rb
+++ /dev/null
@@ -1,113 +0,0 @@
-require 'rubinius/agent'
-
-module ActiveSupport
- module Testing
- module Performance
- DEFAULTS.merge!(
- if ARGV.include?('--benchmark')
- {:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]}
- else
- { :metrics => [:wall_time],
- :formats => [:flat, :graph] }
- end).freeze
-
- protected
- def run_gc
- GC.run(true)
- end
-
- class Performer; end
-
- class Profiler < Performer
- def initialize(*args)
- super
- @supported = @metric.is_a?(Metrics::WallTime)
- end
-
- def run
- return unless @supported
-
- @profiler = Rubinius::Profiler::Instrumenter.new
-
- @total = time_with_block do
- @profiler.profile(false) do
- full_profile_options[:runs].to_i.times { run_test(@metric, :profile) }
- end
- end
- end
-
- def record
- return unless @supported
-
- if(full_profile_options[:formats].include?(:flat))
- create_path_and_open_file(:flat) do |file|
- @profiler.show(file)
- end
- end
-
- if(full_profile_options[:formats].include?(:graph))
- create_path_and_open_file(:graph) do |file|
- @profiler.show(file)
- end
- end
- end
-
- protected
- def create_path_and_open_file(printer_name)
- fname = "#{output_filename}_#{printer_name}.txt"
- FileUtils.mkdir_p(File.dirname(fname))
- File.open(fname, 'wb') do |file|
- yield(file)
- end
- end
- end
-
- module Metrics
- class Base
- attr_reader :loopback
-
- def profile
- yield
- end
-
- protected
- def with_gc_stats
- @loopback = Rubinius::Agent.loopback
- GC.run(true)
- yield
- end
- end
-
- class WallTime < Time
- def measure
- super
- end
- end
-
- class Memory < DigitalInformationUnit
- def measure
- loopback.get("system.memory.counter.bytes").last
- end
- end
-
- class Objects < Amount
- def measure
- loopback.get("system.memory.counter.objects").last
- end
- end
-
- class GcRuns < Amount
- def measure
- loopback.get("system.gc.full.count").last + loopback.get("system.gc.young.count").last
- end
- end
-
- class GcTime < Time
- def measure
- (loopback.get("system.gc.full.wallclock").last + loopback.get("system.gc.young.wallclock").last) / 1000.0
- end
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb
deleted file mode 100644
index 7c149df1e4..0000000000
--- a/activesupport/lib/active_support/testing/performance/ruby.rb
+++ /dev/null
@@ -1,173 +0,0 @@
-begin
- require 'ruby-prof'
-rescue LoadError
- $stderr.puts 'Specify ruby-prof as application\'s dependency in Gemfile to run benchmarks.'
- raise
-end
-
-module ActiveSupport
- module Testing
- module Performance
- DEFAULTS.merge!(
- if ARGV.include?('--benchmark')
- { :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time] }
- else
- { :min_percent => 0.01,
- :metrics => [:process_time, :memory, :objects],
- :formats => [:flat, :graph_html, :call_tree, :call_stack] }
- end).freeze
-
- protected
- remove_method :run_gc
- def run_gc
- GC.start
- end
-
- class Profiler < Performer
- def initialize(*args)
- super
- @supported = @metric.measure_mode rescue false
- end
-
- remove_method :run
- def run
- return unless @supported
-
- RubyProf.measure_mode = @metric.measure_mode
- RubyProf.start
- RubyProf.pause
- full_profile_options[:runs].to_i.times { run_test(@metric, :profile) }
- @data = RubyProf.stop
- @total = @data.threads.sum(0) { |thread| thread.methods.max.total_time }
- end
-
- remove_method :record
- def record
- return unless @supported
-
- klasses = full_profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact
-
- klasses.each do |klass|
- fname = output_filename(klass)
- FileUtils.mkdir_p(File.dirname(fname))
- File.open(fname, 'wb') do |file|
- klass.new(@data).print(file, full_profile_options.slice(:min_percent))
- end
- end
- end
-
- protected
- def output_filename(printer_class)
- suffix =
- case printer_class.name.demodulize
- when 'FlatPrinter'; 'flat.txt'
- when 'FlatPrinterWithLineNumbers'; 'flat_line_numbers.txt'
- when 'GraphPrinter'; 'graph.txt'
- when 'GraphHtmlPrinter'; 'graph.html'
- when 'GraphYamlPrinter'; 'graph.yml'
- when 'CallTreePrinter'; 'tree.txt'
- when 'CallStackPrinter'; 'stack.html'
- when 'DotPrinter'; 'graph.dot'
- else printer_class.name.sub(/Printer$/, '').underscore
- end
-
- "#{super()}_#{suffix}"
- end
- end
-
- module Metrics
- class Base
- def measure_mode
- self.class::Mode
- end
-
- remove_method :profile
- def profile
- RubyProf.resume
- yield
- ensure
- RubyProf.pause
- end
-
- protected
- remove_method :with_gc_stats
- def with_gc_stats
- GC::Profiler.enable
- GC.start
- yield
- ensure
- GC::Profiler.disable
- end
- end
-
- class ProcessTime < Time
- Mode = RubyProf::PROCESS_TIME if RubyProf.const_defined?(:PROCESS_TIME)
-
- def measure
- RubyProf.measure_process_time
- end
- end
-
- class WallTime < Time
- Mode = RubyProf::WALL_TIME if RubyProf.const_defined?(:WALL_TIME)
-
- def measure
- RubyProf.measure_wall_time
- end
- end
-
- class CpuTime < Time
- Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME)
-
- def initialize(*args)
- # FIXME: yeah my CPU is 2.33 GHz
- RubyProf.cpu_frequency = 2.33e9 unless RubyProf.cpu_frequency > 0
- super
- end
-
- def measure
- RubyProf.measure_cpu_time
- end
- end
-
- class Memory < DigitalInformationUnit
- Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY)
-
- # Ruby 1.9 + GCdata patch
- if GC.respond_to?(:malloc_allocated_size)
- def measure
- GC.malloc_allocated_size
- end
- end
- end
-
- class Objects < Amount
- Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS)
-
- # Ruby 1.9 + GCdata patch
- if GC.respond_to?(:malloc_allocations)
- def measure
- GC.malloc_allocations
- end
- end
- end
-
- class GcRuns < Amount
- Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS)
-
- def measure
- GC.count
- end
- end
-
- class GcTime < Time
- Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME)
-
- def measure
- GC::Profiler.total_time
- end
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index fdaaacf2fe..ff13efa990 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -109,6 +109,14 @@ module ActiveSupport
alias_method :gmt_offset, :utc_offset
alias_method :gmtoff, :utc_offset
+ # Returns a formatted string of the offset from UTC, or an alternative
+ # string if the time zone is already UTC.
+ #
+ # Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)"
+ # Time.zone.now.formatted_offset(true) # => "-05:00"
+ # Time.zone.now.formatted_offset(false) # => "-0500"
+ # Time.zone = 'UTC' # => "UTC"
+ # Time.zone.now.formatted_offset(true, "0") # => "0"
def formatted_offset(colon = true, alternate_utc_string = nil)
utc? && alternate_utc_string || TimeZone.seconds_to_utc_offset(utc_offset, colon)
end
@@ -206,18 +214,24 @@ module ActiveSupport
utc <=> other
end
+ # Returns true if the current object's time is within the specified
+ # +min+ and +max+ time.
def between?(min, max)
utc.between?(min, max)
end
+ # Returns true if the current object's time is in the past.
def past?
utc.past?
end
+ # Returns true if the current object's time falls within
+ # the current day.
def today?
time.today?
end
+ # Returns true if the current object's time is in the future.
def future?
utc.future?
end
@@ -303,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/test/caching_test.rb b/activesupport/test/caching_test.rb
index a2e2c51283..5158bbc196 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -675,7 +675,7 @@ class FileStoreTest < ActiveSupport::TestCase
def test_log_exception_when_cache_read_fails
File.expects(:exist?).raises(StandardError, "failed")
@cache.send(:read_entry, "winston", {})
- assert_present @buffer.string
+ assert @buffer.string.present?
end
end
@@ -897,12 +897,12 @@ class CacheStoreLoggerTest < ActiveSupport::TestCase
def test_logging
@cache.fetch('foo') { 'bar' }
- assert_present @buffer.string
+ assert @buffer.string.present?
end
def test_mute_logging
@cache.mute { @cache.fetch('foo') { 'bar' } }
- assert_blank @buffer.string
+ assert @buffer.string.blank?
end
end
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index 5e89a6e00b..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|
@@ -353,6 +357,11 @@ class DateExtBehaviorTest < ActiveSupport::TestCase
Date.today.freeze.freeze
end
end
+
+ def test_compare_with_infinity
+ assert_equal(-1, Date.today <=> Float::INFINITY)
+ assert_equal(1, Date.today <=> -Float::INFINITY)
+ end
end
class DateExtConversionsTest < ActiveSupport::TestCase
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index dfad3a90f8..f74b7b9917 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
@@ -317,3 +320,10 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
end
end
+
+class DateTimeExtBehaviorTest < ActiveSupport::TestCase
+ def test_compare_with_infinity
+ assert_equal(-1, DateTime.now <=> Float::INFINITY)
+ assert_equal(1, DateTime.now <=> -Float::INFINITY)
+ end
+end
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index 2826f51f2d..5e3987265b 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -50,14 +50,12 @@ class DurationTest < ActiveSupport::TestCase
end
def test_argument_error
- begin
- 1.second.ago('')
- flunk("no exception was raised")
- rescue ArgumentError => e
- assert_equal 'expected a time or date, got ""', e.message, "ensure ArgumentError is not being raised by dependencies.rb"
- rescue Exception => e
- flunk("ArgumentError should be raised, but we got #{e.class} instead")
- end
+ 1.second.ago('')
+ flunk("no exception was raised")
+ rescue ArgumentError => e
+ assert_equal 'expected a time or date, got ""', e.message, "ensure ArgumentError is not being raised by dependencies.rb"
+ rescue Exception => e
+ flunk("ArgumentError should be raised, but we got #{e.class} instead")
end
def test_fractional_weeks
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 5fc81ba6fc..30d95b75bc 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -8,7 +8,7 @@ require 'active_support/core_ext/object/deep_dup'
require 'active_support/inflections'
class HashExtTest < ActiveSupport::TestCase
- class IndifferentHash < HashWithIndifferentAccess
+ class IndifferentHash < ActiveSupport::HashWithIndifferentAccess
end
class SubclassingArray < Array
@@ -1015,12 +1015,10 @@ class HashToXmlTest < ActiveSupport::TestCase
<replies-close-in type="integer">2592000000</replies-close-in>
<written-on type="date">2003-07-16</written-on>
<viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
- <content type="yaml">--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n</content>
<author-email-address>david@loudthinking.com</author-email-address>
<parent-id></parent-id>
<ad-revenue type="decimal">1.5</ad-revenue>
<optimum-viewing-angle type="float">135</optimum-viewing-angle>
- <resident type="symbol">yes</resident>
</topic>
EOT
@@ -1033,12 +1031,10 @@ class HashToXmlTest < ActiveSupport::TestCase
:replies_close_in => 2592000000,
:written_on => Date.new(2003, 7, 16),
:viewed_at => Time.utc(2003, 7, 16, 9, 28),
- :content => { :message => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
:author_email_address => "david@loudthinking.com",
:parent_id => nil,
:ad_revenue => BigDecimal("1.50"),
:optimum_viewing_angle => 135.0,
- :resident => :yes
}.stringify_keys
assert_equal expected_topic_hash, Hash.from_xml(topic_xml)["topic"]
@@ -1052,7 +1048,6 @@ class HashToXmlTest < ActiveSupport::TestCase
<approved type="boolean"></approved>
<written-on type="date"></written-on>
<viewed-at type="datetime"></viewed-at>
- <content type="yaml"></content>
<parent-id></parent-id>
</topic>
EOT
@@ -1063,7 +1058,6 @@ class HashToXmlTest < ActiveSupport::TestCase
:approved => nil,
:written_on => nil,
:viewed_at => nil,
- :content => nil,
:parent_id => nil
}.stringify_keys
@@ -1290,6 +1284,28 @@ class HashToXmlTest < ActiveSupport::TestCase
assert_equal expected_product_hash, Hash.from_xml(product_xml)["product"]
end
+ def test_from_xml_raises_on_disallowed_type_attributes
+ assert_raise ActiveSupport::XMLConverter::DisallowedType do
+ Hash.from_xml '<product><name type="foo">value</name></product>', %w(foo)
+ end
+ end
+
+ def test_from_xml_disallows_symbol_and_yaml_types_by_default
+ assert_raise ActiveSupport::XMLConverter::DisallowedType do
+ Hash.from_xml '<product><name type="symbol">value</name></product>'
+ end
+
+ assert_raise ActiveSupport::XMLConverter::DisallowedType do
+ Hash.from_xml '<product><name type="yaml">value</name></product>'
+ end
+ end
+
+ def test_from_trusted_xml_allows_symbol_and_yaml_types
+ expected = { 'product' => { 'name' => :value }}
+ assert_equal expected, Hash.from_trusted_xml('<product><name type="symbol">value</name></product>')
+ assert_equal expected, Hash.from_trusted_xml('<product><name type="yaml">:value</name></product>')
+ end
+
def test_should_use_default_value_for_unknown_key
hash_wia = HashWithIndifferentAccess.new(3)
assert_equal 3, hash_wia[:new_key]
diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb
index 435f4aa5a1..8c7d00cae1 100644
--- a/activesupport/test/core_ext/numeric_ext_test.rb
+++ b/activesupport/test/core_ext/numeric_ext_test.rb
@@ -203,7 +203,7 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
def terabytes(number)
gigabytes(number) * 1024
end
-
+
def test_to_s__phone
assert_equal("555-1234", 5551234.to_s(:phone))
assert_equal("800-555-1212", 8005551212.to_s(:phone))
@@ -217,7 +217,7 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
assert_equal("22-555-1212", 225551212.to_s(:phone))
assert_equal("+45-22-555-1212", 225551212.to_s(:phone, :country_code => 45))
end
-
+
def test_to_s__currency
assert_equal("$1,234,567,890.50", 1234567890.50.to_s(:currency))
assert_equal("$1,234,567,890.51", 1234567890.506.to_s(:currency))
@@ -228,8 +228,8 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
assert_equal("$1,234,567,890.5", 1234567890.50.to_s(:currency, :precision => 1))
assert_equal("&pound;1234567890,50", 1234567890.50.to_s(:currency, :unit => "&pound;", :separator => ",", :delimiter => ""))
end
-
-
+
+
def test_to_s__rounded
assert_equal("-111.235", -111.2346.to_s(:rounded))
assert_equal("111.235", 111.2346.to_s(:rounded))
@@ -246,7 +246,7 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
assert_equal("11.00", 10.995.to_s(:rounded, :precision => 2))
assert_equal("0.00", -0.001.to_s(:rounded, :precision => 2))
end
-
+
def test_to_s__percentage
assert_equal("100.000%", 100.to_s(:percentage))
assert_equal("100%", 100.to_s(:percentage, :precision => 0))
@@ -274,7 +274,7 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
assert_equal '12.345.678,05', 12345678.05.to_s(:delimited, :separator => ',', :delimiter => '.')
assert_equal '12.345.678,05', 12345678.05.to_s(:delimited, :delimiter => '.', :separator => ',')
end
-
+
def test_to_s__rounded_with_custom_delimiter_and_separator
assert_equal '31,83', 31.825.to_s(:rounded, :precision => 2, :separator => ',')
@@ -350,7 +350,7 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
assert_equal '1.23 GB', 1234567890.to_s(:human_size, :prefix => :si)
assert_equal '1.23 TB', 1234567890123.to_s(:human_size, :prefix => :si)
end
-
+
def test_to_s__human_size_with_options_hash
assert_equal '1.2 MB', 1234567.to_s(:human_size, :precision => 2)
assert_equal '3 Bytes', 3.14159265.to_s(:human_size, :precision => 4)
@@ -366,13 +366,13 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
assert_equal '1.012 KB', kilobytes(1.0123).to_s(:human_size, :precision => 3, :significant => false)
assert_equal '1 KB', kilobytes(1.0123).to_s(:human_size, :precision => 0, :significant => true) #ignores significant it precision is 0
end
-
+
def test_to_s__human_size_with_custom_delimiter_and_separator
assert_equal '1,01 KB', kilobytes(1.0123).to_s(:human_size, :precision => 3, :separator => ',')
assert_equal '1,01 KB', kilobytes(1.0100).to_s(:human_size, :precision => 4, :separator => ',')
assert_equal '1.000,1 TB', terabytes(1000.1).to_s(:human_size, :precision => 5, :delimiter => '.', :separator => ',')
end
-
+
def test_number_to_human
assert_equal '-123', -123.to_s(:human)
assert_equal '-0.5', -0.5.to_s(:human)
@@ -436,7 +436,7 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
def test_to_s__injected_on_proper_types
assert_equal Fixnum, 1230.class
assert_equal '1.23 Thousand', 1230.to_s(:human)
-
+
assert_equal Float, Float(1230).class
assert_equal '1.23 Thousand', Float(1230).to_s(:human)
@@ -447,3 +447,57 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
assert_equal '1 Million', BigDecimal("1000010").to_s(:human)
end
end
+
+class NumericExtBehaviorTest < ActiveSupport::TestCase
+ def setup
+ @inf = BigDecimal.new('Infinity')
+ end
+
+ def test_compare_infinity_with_date
+ assert_equal(-1, -Float::INFINITY <=> Date.today)
+ assert_equal(1, Float::INFINITY <=> Date.today)
+ assert_equal(-1, -@inf <=> Date.today)
+ assert_equal(1, @inf <=> Date.today)
+ end
+
+ def test_compare_infinty_with_infinty
+ assert_equal(-1, -Float::INFINITY <=> Float::INFINITY)
+ assert_equal(1, Float::INFINITY <=> -Float::INFINITY)
+ assert_equal(0, Float::INFINITY <=> Float::INFINITY)
+ assert_equal(0, -Float::INFINITY <=> -Float::INFINITY)
+
+ assert_equal(-1, -Float::INFINITY <=> BigDecimal::INFINITY)
+ assert_equal(1, Float::INFINITY <=> -BigDecimal::INFINITY)
+ assert_equal(0, Float::INFINITY <=> BigDecimal::INFINITY)
+ assert_equal(0, -Float::INFINITY <=> -BigDecimal::INFINITY)
+
+ assert_equal(-1, -BigDecimal::INFINITY <=> Float::INFINITY)
+ assert_equal(1, BigDecimal::INFINITY <=> -Float::INFINITY)
+ assert_equal(0, BigDecimal::INFINITY <=> Float::INFINITY)
+ assert_equal(0, -BigDecimal::INFINITY <=> -Float::INFINITY)
+ end
+
+ def test_compare_infinity_with_time
+ assert_equal(-1, -Float::INFINITY <=> Time.now)
+ assert_equal(1, Float::INFINITY <=> Time.now)
+ assert_equal(-1, -@inf <=> Time.now)
+ assert_equal(1, @inf <=> Time.now)
+ end
+
+ def test_compare_infinity_with_datetime
+ assert_equal(-1, -Float::INFINITY <=> DateTime.now)
+ assert_equal(1, Float::INFINITY <=> DateTime.now)
+ assert_equal(-1, -@inf <=> DateTime.now)
+ assert_equal(1, @inf <=> DateTime.now)
+ end
+
+ def test_compare_infinity_with_twz
+ time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = ActiveSupport::TimeWithZone.new(Time.now, time_zone)
+
+ assert_equal(-1, -Float::INFINITY <=> twz)
+ assert_equal(1, Float::INFINITY <=> twz)
+ assert_equal(-1, -@inf <=> twz)
+ assert_equal(1, @inf <=> twz)
+ end
+end
diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb
index f0cdc0bfd4..6e94d5e10d 100644
--- a/activesupport/test/core_ext/range_ext_test.rb
+++ b/activesupport/test/core_ext/range_ext_test.rb
@@ -1,6 +1,7 @@
require 'abstract_unit'
require 'active_support/time'
require 'active_support/core_ext/range'
+require 'active_support/core_ext/numeric'
class RangeTest < ActiveSupport::TestCase
def test_to_s_from_dates
@@ -12,6 +13,12 @@ class RangeTest < ActiveSupport::TestCase
date_range = Time.utc(2005, 12, 10, 15, 30)..Time.utc(2005, 12, 10, 17, 30)
assert_equal "BETWEEN '2005-12-10 15:30:00' AND '2005-12-10 17:30:00'", date_range.to_s(:db)
end
+
+ def test_date_range
+ assert_instance_of Range, DateTime.new..DateTime.new
+ assert_instance_of Range, DateTime::Infinity.new..DateTime::Infinity.new
+ assert_instance_of Range, DateTime.new..DateTime::Infinity.new
+ end
def test_overlaps_last_inclusive
assert((1..5).overlaps?(5..10))
@@ -85,4 +92,28 @@ class RangeTest < ActiveSupport::TestCase
time_range_2 = Time.utc(2005, 12, 10, 17, 31)..Time.utc(2005, 12, 10, 18, 00)
assert !time_range_1.overlaps?(time_range_2)
end
+
+ def test_infinite_bounds
+ time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+
+ time = Time.now
+ date = Date.today
+ datetime = DateTime.now
+ twz = ActiveSupport::TimeWithZone.new(time, time_zone)
+
+ infinity1 = Float::INFINITY
+ infinity2 = BigDecimal.new('Infinity')
+
+ [infinity1, infinity2].each do |infinity|
+ [time, date, datetime, twz].each do |bound|
+ [time, date, datetime, twz].each do |value|
+ assert Range.new(bound, infinity).include?(value + 10.years)
+ assert Range.new(-infinity, bound).include?(value - 10.years)
+
+ assert !Range.new(bound, infinity).include?(value - 10.years)
+ assert !Range.new(-infinity, bound).include?(value + 10.years)
+ end
+ end
+ end
+ end
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index fa8839bcb3..db1cf14abf 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -286,14 +286,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,10 +327,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
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index 70e07cefd1..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,
@@ -843,3 +847,10 @@ class TimeExtMarshalingTest < ActiveSupport::TestCase
assert_equal Time.local(2004, 2, 29), Time.local(2004, 5, 31).last_quarter
end
end
+
+class TimeExtBehaviorTest < ActiveSupport::TestCase
+ def test_compare_with_infinity
+ assert_equal(-1, Time.now <=> Float::INFINITY)
+ assert_equal(1, Time.now <=> -Float::INFINITY)
+ end
+end
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index 56020da035..18eca4cd8b 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -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
@@ -1081,3 +1085,13 @@ class TimeWithZoneMethodsForString < ActiveSupport::TestCase
Time.zone = old_tz
end
end
+
+class TimeWithZoneExtBehaviorTest < ActiveSupport::TestCase
+ def test_compare_with_infinity
+ time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
+ twz = ActiveSupport::TimeWithZone.new(Time.now, time_zone)
+
+ assert_equal(-1, twz <=> Float::INFINITY)
+ assert_equal(1, twz <=> -Float::INFINITY)
+ end
+end
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index 9d913c27b0..615808090d 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -363,12 +363,10 @@ class DependenciesTest < ActiveSupport::TestCase
end
def test_smart_name_error_strings
- begin
- Object.module_eval "ImaginaryObject"
- flunk "No raise!!"
- rescue NameError => e
- assert e.message.include?("uninitialized constant ImaginaryObject")
- end
+ Object.module_eval "ImaginaryObject"
+ flunk "No raise!!"
+ rescue NameError => e
+ assert e.message.include?("uninitialized constant ImaginaryObject")
end
def test_loadable_constants_for_path_should_handle_empty_autoloads
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index 332100f5a1..c1a468ec86 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -119,9 +119,10 @@ class DeprecationTest < ActiveSupport::TestCase
ActiveSupport::Deprecation.behavior = :silence
behavior = ActiveSupport::Deprecation.behavior.first
- assert_blank capture(:stderr) {
+ stderr_output = capture(:stderr) {
assert_nil behavior.call('Some error!', ['call stack!'])
}
+ assert stderr_output.blank?
end
def test_deprecated_instance_variable_proxy
@@ -254,10 +255,10 @@ class DeprecationTest < ActiveSupport::TestCase
klass::OLD.to_s
end
end
-
+
def test_deprecated_instance_variable_with_instance_deprecator
deprecator = deprecator_with_messages
-
+
klass = Class.new() do
def initialize(deprecator)
@request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator)
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.rb b/activesupport/test/inflector_test.rb
index aa41e57928..a1e5db6a2e 100644
--- a/activesupport/test/inflector_test.rb
+++ b/activesupport/test/inflector_test.rb
@@ -361,7 +361,7 @@ class InflectorTest < ActiveSupport::TestCase
inflect.singular(/s$/, '')
inflect.singular(/es$/, '')
-
+
inflect.irregular('el', 'los')
end
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/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb
index d6c31396b6..06c7e8a1a7 100644
--- a/activesupport/test/message_encryptor_test.rb
+++ b/activesupport/test/message_encryptor_test.rb
@@ -29,9 +29,9 @@ class MessageEncryptorTest < ActiveSupport::TestCase
end
def test_encrypting_twice_yields_differing_cipher_text
- first_messqage = @encryptor.encrypt_and_sign(@data).split("--").first
+ first_message = @encryptor.encrypt_and_sign(@data).split("--").first
second_message = @encryptor.encrypt_and_sign(@data).split("--").first
- assert_not_equal first_messqage, second_message
+ assert_not_equal first_message, second_message
end
def test_messing_with_either_encrypted_values_causes_failure
diff --git a/activesupport/test/notifications/instrumenter_test.rb b/activesupport/test/notifications/instrumenter_test.rb
new file mode 100644
index 0000000000..62a9b61464
--- /dev/null
+++ b/activesupport/test/notifications/instrumenter_test.rb
@@ -0,0 +1,50 @@
+require 'abstract_unit'
+require 'active_support/notifications/instrumenter'
+
+module ActiveSupport
+ module Notifications
+ class InstrumenterTest < ActiveSupport::TestCase
+ class TestNotifier
+ attr_reader :starts, :finishes
+
+ def initialize
+ @starts = []
+ @finishes = []
+ end
+
+ def start(*args); @starts << args; end
+ def finish(*args); @finishes << args; end
+ end
+
+ attr_reader :instrumenter, :notifier, :payload
+
+ def setup
+ super
+ @notifier = TestNotifier.new
+ @instrumenter = Instrumenter.new @notifier
+ @payload = { :foo => Object.new }
+ end
+
+ def test_instrument
+ called = false
+ instrumenter.instrument("foo", payload) {
+ called = true
+ }
+
+ assert called
+ end
+
+ def test_start
+ instrumenter.start("foo", payload)
+ assert_equal [["foo", instrumenter.id, payload]], notifier.starts
+ assert_predicate notifier.finishes, :empty?
+ end
+
+ def test_finish
+ instrumenter.finish("foo", payload)
+ assert_equal [["foo", instrumenter.id, payload]], notifier.finishes
+ assert_predicate notifier.starts, :empty?
+ end
+ end
+ end
+end
diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb
index 0f93c8b59e..3e6ac811a4 100644
--- a/activesupport/test/test_test.rb
+++ b/activesupport/test/test_test.rb
@@ -92,17 +92,21 @@ class AssertBlankTest < ActiveSupport::TestCase
NOT_BLANK = [ EmptyFalse.new, Object.new, true, 0, 1, 'x', [nil], { nil => 0 } ]
def test_assert_blank_true
- BLANK.each { |v| assert_blank v }
+ BLANK.each { |value|
+ assert_deprecated { assert_blank value }
+ }
end
def test_assert_blank_false
NOT_BLANK.each { |v|
- begin
- assert_blank v
- fail 'should not get to here'
- rescue Exception => e
- assert_match(/is not blank/, e.message)
- end
+ assert_deprecated {
+ begin
+ assert_blank v
+ fail 'should not get to here'
+ rescue Exception => e
+ assert_match(/is not blank/, e.message)
+ end
+ }
}
end
end
@@ -112,17 +116,21 @@ class AssertPresentTest < ActiveSupport::TestCase
NOT_BLANK = [ EmptyFalse.new, Object.new, true, 0, 1, 'x', [nil], { nil => 0 } ]
def test_assert_present_true
- NOT_BLANK.each { |v| assert_present v }
+ NOT_BLANK.each { |v|
+ assert_deprecated { assert_present v }
+ }
end
def test_assert_present_false
BLANK.each { |v|
- begin
- assert_present v
- fail 'should not get to here'
- rescue Exception => e
- assert_match(/is blank/, e.message)
- end
+ assert_deprecated {
+ begin
+ assert_present v
+ fail 'should not get to here'
+ rescue Exception => e
+ assert_match(/is blank/, e.message)
+ end
+ }
}
end
end
diff --git a/activesupport/test/testing/performance_test.rb b/activesupport/test/testing/performance_test.rb
deleted file mode 100644
index 6918110cce..0000000000
--- a/activesupport/test/testing/performance_test.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require 'abstract_unit'
-
-module ActiveSupport
- module Testing
- class PerformanceTest < ActiveSupport::TestCase
- begin
- require 'active_support/testing/performance'
- HAVE_RUBYPROF = true
- rescue LoadError
- HAVE_RUBYPROF = false
- end
-
- def setup
- skip "no rubyprof" unless HAVE_RUBYPROF
- end
-
- def test_amount_format
- amount_metric = ActiveSupport::Testing::Performance::Metrics[:amount].new
- assert_equal "0", amount_metric.format(0)
- assert_equal "1", amount_metric.format(1.23)
- assert_equal "40,000,000", amount_metric.format(40000000)
- end
-
- def test_time_format
- time_metric = ActiveSupport::Testing::Performance::Metrics[:time].new
- assert_equal "0 ms", time_metric.format(0)
- assert_equal "40 ms", time_metric.format(0.04)
- assert_equal "41 ms", time_metric.format(0.0415)
- assert_equal "1.23 sec", time_metric.format(1.23)
- assert_equal "40000.00 sec", time_metric.format(40000)
- assert_equal "-5000 ms", time_metric.format(-5)
- end
-
- def test_space_format
- space_metric = ActiveSupport::Testing::Performance::Metrics[:digital_information_unit].new
- assert_equal "0 Bytes", space_metric.format(0)
- assert_equal "0 Bytes", space_metric.format(0.4)
- assert_equal "1 Byte", space_metric.format(1.23)
- assert_equal "123 Bytes", space_metric.format(123)
- assert_equal "123 Bytes", space_metric.format(123.45)
- assert_equal "12 KB", space_metric.format(12345)
- assert_equal "1.2 MB", space_metric.format(1234567)
- assert_equal "9.3 GB", space_metric.format(10**10)
- assert_equal "91 TB", space_metric.format(10**14)
- assert_equal "910000 TB", space_metric.format(10**18)
- end
-
- def test_environment_format_without_rails
- metric = ActiveSupport::Testing::Performance::Metrics[:time].new
- benchmarker = ActiveSupport::Testing::Performance::Benchmarker.new(self, metric)
- assert_equal "#{RUBY_ENGINE}-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL},#{RUBY_PLATFORM}", benchmarker.environment
- end
-
- def test_environment_format_with_rails
- rails, version = Module.new, Module.new
- version.const_set :STRING, "4.0.0"
- rails.const_set :VERSION, version
- Object.const_set :Rails, rails
-
- metric = ActiveSupport::Testing::Performance::Metrics[:time].new
- benchmarker = ActiveSupport::Testing::Performance::Benchmarker.new(self, metric)
- assert_equal "rails-4.0.0,#{RUBY_ENGINE}-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL},#{RUBY_PLATFORM}", benchmarker.environment
- ensure
- Object.send :remove_const, :Rails
- end
- end
- end
-end
diff --git a/guides/assets/stylesheets/main.css b/guides/assets/stylesheets/main.css
index 589c96e0e9..dd029e6314 100644
--- a/guides/assets/stylesheets/main.css
+++ b/guides/assets/stylesheets/main.css
@@ -83,6 +83,10 @@ table th {
padding: 0.5em 1em;
}
+img {
+ max-width: 100%;
+}
+
/* Structure and Layout
--------------------------------------- */
@@ -573,7 +577,7 @@ h6 {
#mainCol div.warning, #subCol dd.warning {
background: #f9d9d8 url(../images/tab_red.gif) no-repeat left top;
border: none;
- padding: 1.25em 1.25em 1.25em 48px;
+ padding: 1.25em 1.25em 0.25em 48px;
margin-left: 0;
margin-top: 0.25em;
}
diff --git a/guides/code/getting_started/README.rdoc b/guides/code/getting_started/README.rdoc
index b5d7b6436b..232856ca5f 100644
--- a/guides/code/getting_started/README.rdoc
+++ b/guides/code/getting_started/README.rdoc
@@ -1,259 +1,28 @@
-== Welcome to Rails
+== README
-Rails is a web-application framework that includes everything needed to create
-database-backed web applications according to the Model-View-Control pattern.
+This README would normally document whatever steps are necessary to get the
+application up and running.
-This pattern splits the view (also called the presentation) into "dumb"
-templates that are primarily responsible for inserting pre-built data in between
-HTML tags. The model contains the "smart" domain objects (such as Account,
-Product, Person, Post) that holds all the business logic and knows how to
-persist themselves to a database. The controller handles the incoming requests
-(such as Save New Account, Update Product, Show Post) by manipulating the model
-and directing data to the view.
+Things you may want to cover:
-In Rails, the model is handled by what's called an object-relational mapping
-layer entitled Active Record. This layer allows you to present the data from
-database rows as objects and embellish these data objects with business logic
-methods. You can read more about Active Record in
-link:files/vendor/rails/activerecord/README.html.
+* Ruby version
-The controller and view are handled by the Action Pack, which handles both
-layers by its two parts: Action View and Action Controller. These two layers
-are bundled in a single package due to their heavy interdependence. This is
-unlike the relationship between the Active Record and Action Pack that is much
-more separate. Each of these packages can be used independently outside of
-Rails. You can read more about Action Pack in
-link:files/vendor/rails/actionpack/README.html.
+* System dependencies
+* Configuration
-== Getting Started
+* Database creation
-1. At the command prompt, create a new Rails application:
- <tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
+* Database initialization
-2. Change directory to <tt>myapp</tt> and start the web server:
- <tt>cd myapp; rails server</tt> (run with --help for options)
+* How to run the test suite
-3. Go to http://localhost:3000/ and you'll see:
- "Welcome aboard: You're riding Ruby on Rails!"
+* Services (job queues, cache servers, search engines, etc.)
-4. Follow the guidelines to start developing your application. You can find
-the following resources handy:
+* Deployment instructions
-* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
-* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
+* ...
-
-== Debugging Rails
-
-Sometimes your application goes wrong. Fortunately there are a lot of tools that
-will help you debug it and get it back on the rails.
-
-First area to check is the application log files. Have "tail -f" commands
-running on the server.log and development.log. Rails will automatically display
-debugging and runtime information to these files. Debugging info will also be
-shown in the browser on requests from 127.0.0.1.
-
-You can also log your own messages directly into the log file from your code
-using the Ruby logger class from inside your controllers. Example:
-
- class WeblogController < ActionController::Base
- def destroy
- @weblog = Weblog.find(params[:id])
- @weblog.destroy
- logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
- end
- end
-
-The result will be a message in your log file along the lines of:
-
- Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
-
-More information on how to use the logger is at http://www.ruby-doc.org/core/
-
-Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
-several books available online as well:
-
-* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
-* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
-
-These two books will bring you up to speed on the Ruby language and also on
-programming in general.
-
-
-== Debugger
-
-Debugger support is available through the debugger command when you start your
-Mongrel or WEBrick server with --debugger. This means that you can break out of
-execution at any point in the code, investigate and change the model, and then,
-resume execution! You need to install the 'debugger' gem to run the server in debugging
-mode. Add gem 'debugger' to your Gemfile and run <tt>bundle</tt> to install it. Example:
-
- class WeblogController < ActionController::Base
- def index
- @posts = Post.all
- debugger
- end
- end
-
-So the controller will accept the action, run the first line, then present you
-with a IRB prompt in the server window. Here you can do things like:
-
- >> @posts.inspect
- => "[#<Post:0x14a6be8
- @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>,
- #<Post:0x14a6620
- @attributes={"title"=>"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
- >> @posts.first.title = "hello from a debugger"
- => "hello from a debugger"
-
-...and even better, you can examine how your runtime objects actually work:
-
- >> f = @posts.first
- => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
- >> f.
- Display all 152 possibilities? (y or n)
-
-Finally, when you're ready to resume execution, you can enter "cont".
-
-
-== Console
-
-The console is a Ruby shell, which allows you to interact with your
-application's domain model. Here you'll have all parts of the application
-configured, just like it is when the application is running. You can inspect
-domain models, change values, and save to the database. Starting the script
-without arguments will launch it in the development environment.
-
-To start the console, run <tt>rails console</tt> from the application
-directory.
-
-Options:
-
-* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications
- made to the database.
-* Passing an environment name as an argument will load the corresponding
- environment. Example: <tt>rails console production</tt>.
-
-To reload your controllers and models after launching the console run
-<tt>reload!</tt>
-
-More information about irb can be found at:
-link:http://www.rubycentral.org/pickaxe/irb.html
-
-
-== dbconsole
-
-You can go to the command line of your database directly through <tt>rails
-dbconsole</tt>. You would be connected to the database with the credentials
-defined in database.yml. Starting the script without arguments will connect you
-to the development database. Passing an argument will connect you to a different
-database, like <tt>rails dbconsole production</tt>. Currently works for MySQL,
-PostgreSQL and SQLite 3.
-
-== Description of Contents
-
-The default directory structure of a generated Ruby on Rails application:
-
- |-- app
- | |-- assets
- | |-- images
- | |-- javascripts
- | `-- stylesheets
- | |-- controllers
- | |-- helpers
- | |-- mailers
- | |-- models
- | `-- views
- | `-- layouts
- |-- config
- | |-- environments
- | |-- initializers
- | `-- locales
- |-- db
- |-- doc
- |-- lib
- | `-- tasks
- |-- log
- |-- public
- |-- script
- |-- test
- | |-- fixtures
- | |-- functional
- | |-- integration
- | |-- performance
- | `-- unit
- |-- tmp
- | |-- cache
- | |-- pids
- | |-- sessions
- | `-- sockets
- `-- vendor
- |-- assets
- `-- stylesheets
-
-app
- Holds all the code that's specific to this particular application.
-
-app/assets
- Contains subdirectories for images, stylesheets, and JavaScript files.
-
-app/controllers
- Holds controllers that should be named like weblogs_controller.rb for
- automated URL mapping. All controllers should descend from
- ApplicationController which itself descends from ActionController::Base.
-
-app/models
- Holds models that should be named like post.rb. Models descend from
- ActiveRecord::Base by default.
-
-app/views
- Holds the template files for the view that should be named like
- weblogs/index.html.erb for the WeblogsController#index action. All views use
- eRuby syntax by default.
-
-app/views/layouts
- Holds the template files for layouts to be used with views. This models the
- common header/footer method of wrapping views. In your views, define a layout
- using the <tt>layout :default</tt> and create a file named default.html.erb.
- Inside default.html.erb, call <% yield %> to render the view using this
- layout.
-
-app/helpers
- Holds view helpers that should be named like weblogs_helper.rb. These are
- generated for you automatically when using generators for controllers.
- Helpers can be used to wrap functionality for your views into methods.
-
-config
- Configuration files for the Rails environment, the routing map, the database,
- and other dependencies.
-
-db
- Contains the database schema in schema.rb. db/migrate contains all the
- sequence of Migrations for your schema.
-
-doc
- This directory is where your application documentation will be stored when
- generated using <tt>rake doc:app</tt>
-
-lib
- Application specific libraries. Basically, any kind of custom code that
- doesn't belong under controllers, models, or helpers. This directory is in
- the load path.
-
-public
- The directory available for the web server. Also contains the dispatchers and the
- default HTML files. This should be set as the DOCUMENT_ROOT of your web
- server.
-
-script
- Helper scripts for automation and generation.
-
-test
- Unit and functional tests along with fixtures. When using the rails generate
- command, template test files will be generated for you and placed in this
- directory.
-
-vendor
- External libraries that the application depends on. If the app has frozen rails,
- those gems also go here, under vendor/rails/. This directory is in the load path.
+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
diff --git a/guides/code/getting_started/doc/README_FOR_APP b/guides/code/getting_started/doc/README_FOR_APP
deleted file mode 100644
index fe41f5cc24..0000000000
--- a/guides/code/getting_started/doc/README_FOR_APP
+++ /dev/null
@@ -1,2 +0,0 @@
-Use this README file to introduce your application and point to useful places in the API for learning more.
-Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries.
diff --git a/guides/code/getting_started/test/performance/browsing_test.rb b/guides/code/getting_started/test/performance/browsing_test.rb
deleted file mode 100644
index 9342a57b20..0000000000
--- a/guides/code/getting_started/test/performance/browsing_test.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require 'test_helper'
-require 'rails/performance_test_help'
-
-class BrowsingTest < ActionDispatch::PerformanceTest
- # Refer to the documentation for all available options
- # self.profile_options = { runs: 5, metrics: [:wall_time, :memory],
- # output: 'tmp/performance', formats: [:flat] }
-
- def test_homepage
- get '/'
- end
-end
diff --git a/guides/rails_guides/markdown.rb b/guides/rails_guides/markdown.rb
index 650489e6cb..547c6d2c15 100644
--- a/guides/rails_guides/markdown.rb
+++ b/guides/rails_guides/markdown.rb
@@ -1,3 +1,5 @@
+# encoding: utf-8
+
require 'redcarpet'
require 'nokogiri'
require 'rails_guides/markdown/renderer'
@@ -129,7 +131,7 @@ module RailsGuides
def generate_title
if heading = Nokogiri::HTML(@header).at(:h2)
- @title = "Ruby on Rails Guides: #{heading.text}".html_safe
+ @title = "#{heading.text} — Ruby on Rails Guides".html_safe
else
@title = "Ruby on Rails Guides"
end
diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md
index 34d2c09812..52571fab60 100644
--- a/guides/source/4_0_release_notes.md
+++ b/guides/source/4_0_release_notes.md
@@ -68,6 +68,7 @@ In Rails 4.0, several features have been extracted into gems. You can simply add
* Action Caching ([Github](https://github.com/rails/actionpack-action_caching), [Pull Request](https://github.com/rails/rails/pull/7833))
* Page Caching ([Github](https://github.com/rails/actionpack-page_caching), [Pull Request](https://github.com/rails/rails/pull/7833))
* Sprockets ([Github](https://github.com/rails/sprockets-rails))
+* Performance tests ([Github](https://github.com/rails/rails-perftest), [Pull Request](https://github.com/rails/rails/pull/8876))
Documentation
-------------
@@ -85,6 +86,8 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/railt
* New test locations `test/models`, `test/helpers`, `test/controllers`, and `test/mailers`. Corresponding rake tasks added as well. ([Pull Request](https://github.com/rails/rails/pull/7878))
+* Your app's executables now live in the `bin/` dir. Run `rake rails:update:bin` to get `bin/bundle`, `bin/rails`, and `bin/rake`.
+
* Threadsafe on by default
### Deprecations
@@ -142,6 +145,7 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activ
* BufferedLogger is deprecated. Use ActiveSupport::Logger, or the logger from Ruby stdlib.
+* Deprecate `assert_present` and `assert_blank` in favor of `assert object.blank?` and `assert object.present?`
Action Pack
-----------
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 46ff9027fd..cc80334af3 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -114,7 +114,7 @@ To send a hash you include the key name inside the brackets:
When this form is submitted, the value of `params[:client]` will be `{"name" => "Acme", "phone" => "12345", "address" => {"postcode" => "12345", "city" => "Carrot City"}}`. Note the nested hash in `params[:client][:address]`.
-Note that the `params` hash is actually an instance of `HashWithIndifferentAccess` from Active Support, which acts like a hash that lets you use symbols and strings interchangeably as keys.
+Note that the `params` hash is actually an instance of `ActiveSupport::HashWithIndifferentAccess`, which acts like a hash that lets you use symbols and strings interchangeably as keys.
### JSON/XML parameters
@@ -174,10 +174,10 @@ Session
Your application has a session for each user in which you can store small amounts of data that will be persisted between requests. The session is only available in the controller and the view and can use one of a number of different storage mechanisms:
-* ActionDispatch::Session::CookieStore - Stores everything on the client.
-* ActionDispatch::Session::CacheStore - Stores the data in the Rails cache.
-* @ActionDispatch::Session::ActiveRecordStore@ - Stores the data in a database using Active Record. (require `activerecord-session_store` gem).
-* @ActionDispatch::Session::MemCacheStore@ - Stores the data in a memcached cluster (this is a legacy implementation; consider using CacheStore instead).
+* `ActionDispatch::Session::CookieStore` - Stores everything on the client.
+* `ActionDispatch::Session::CacheStore` - Stores the data in the Rails cache.
+* `ActionDispatch::Session::ActiveRecordStore` - Stores the data in a database using Active Record. (require `activerecord-session_store` gem).
+* `ActionDispatch::Session::MemCacheStore` - Stores the data in a memcached cluster (this is a legacy implementation; consider using CacheStore instead).
All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure).
@@ -194,7 +194,7 @@ If you need a different session storage mechanism, you can change it in the `con
```ruby
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
-# (create the session table with "script/rails g active_record:session_migration")
+# (create the session table with "rails g active_record:session_migration")
# YourApp::Application.config.session_store :active_record_store
```
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index 795afd0150..3a9a4e73a6 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -419,7 +419,7 @@ Receiving and parsing emails with Action Mailer can be a rather complex endeavor
* Implement a `receive` method in your mailer.
-* Configure your email server to forward emails from the address(es) you would like your app to receive to `/path/to/app/script/rails runner 'UserMailer.receive(STDIN.read)'`.
+* Configure your email server to forward emails from the address(es) you would like your app to receive to `/path/to/app/bin/rails runner 'UserMailer.receive(STDIN.read)'`.
Once a method called `receive` is defined in any mailer, Action Mailer will parse the raw incoming email into an email object, decode it, instantiate a new mailer, and pass the email object to the mailer `receive` instance method. Here's an example:
@@ -575,3 +575,23 @@ 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.
+
+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 c90f42c492..062bcd49f4 100644
--- a/guides/source/active_record_basics.md
+++ b/guides/source/active_record_basics.md
@@ -239,12 +239,12 @@ Active Record provides a rich API for accessing data within a database. Below
are a few examples of different data access methods provided by Active Record.
```ruby
-# return array with all records
+# return a collection with all users
users = User.all
```
```ruby
-# return the first record
+# return the first user
user = User.first
```
diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md
index 20959a1a35..3747b00b82 100644
--- a/guides/source/active_record_callbacks.md
+++ b/guides/source/active_record_callbacks.md
@@ -168,7 +168,6 @@ Additionally, the `after_find` callback is triggered by the following finder met
* `all`
* `first`
* `find`
-* `find_all_by_*`
* `find_by_*`
* `find_by_*!`
* `find_by_sql`
@@ -176,7 +175,7 @@ Additionally, the `after_find` callback is triggered by the following finder met
The `after_initialize` callback is triggered every time a new object of the class is initialized.
-NOTE: The `find_all_by_*`, `find_by_*` and `find_by_*!` methods are dynamic finders generated automatically for every attribute. Learn more about them at the [Dynamic finders section](active_record_querying.html#dynamic-finders)
+NOTE: The `find_by_*` and `find_by_*!` methods are dynamic finders generated automatically for every attribute. Learn more about them at the [Dynamic finders section](active_record_querying.html#dynamic-finders)
Skipping Callbacks
------------------
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 24f98f68ca..62d6294ae5 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -1228,9 +1228,7 @@ Client.unscoped {
Dynamic Finders
---------------
-For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called `first_name` on your `Client` model for example, you get `find_by_first_name` and `find_all_by_first_name` for free from Active Record. If you have a `locked` field on the `Client` model, you also get `find_by_locked` and `find_all_by_locked` methods.
-
-You can also use `find_last_by_*` methods which will find the last record matching your argument.
+For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called `first_name` on your `Client` model for example, you get `find_by_first_name` for free from Active Record. If you have a `locked` field on the `Client` model, you also get `find_by_locked` and methods.
You can specify an exclamation point (`!`) on the end of the dynamic finders to get them to raise an `ActiveRecord::RecordNotFound` error if they do not return any records, like `Client.find_by_name!("Ryan")`
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index a911d6b941..eaa47d4ebf 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -117,7 +117,6 @@ database only if the object is valid:
* `save`
* `save!`
* `update`
-* `update`
* `update!`
The bang versions (e.g. `save!`) raise an exception if the record is invalid.
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 23736020ec..3c1bb0f132 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -422,24 +422,6 @@ NOTE: Defined in `active_support/core_ext/object/with_options.rb`.
Active Support provides several methods to ease access to instance variables.
-#### `instance_variable_names`
-
-Ruby 1.8 and 1.9 have a method called `instance_variables` that returns the names of the defined instance variables. But they behave differently, in 1.8 it returns strings whereas in 1.9 it returns symbols. Active Support defines `instance_variable_names` as a portable way to obtain them as strings:
-
-```ruby
-class C
- def initialize(x, y)
- @x, @y = x, y
- end
-end
-
-C.new(0, 1).instance_variable_names # => ["@y", "@x"]
-```
-
-WARNING: The order in which the names are returned is unspecified, and it indeed depends on the version of the interpreter.
-
-NOTE: Defined in `active_support/core_ext/object/instance_variables.rb`.
-
#### `instance_values`
The method `instance_values` returns a hash that maps instance variable names without "@" to their
@@ -920,7 +902,7 @@ When interpolated into a string, the `:to` option should become an expression th
delegate :logger, to: :Rails
# delegates to the receiver's class
-delegate :table_name, to: 'self.class'
+delegate :table_name, to: :class
```
WARNING: If the `:prefix` option is `true` this is less generic, see below.
@@ -1449,11 +1431,10 @@ As the previous example shows, Active Support knows some irregular plurals and u
Active Record uses this method to compute the default table name that corresponds to a model:
```ruby
-# active_record/base.rb
+# active_record/model_schema.rb
def undecorated_table_name(class_name = base_class.name)
table_name = class_name.to_s.demodulize.underscore
- table_name = table_name.pluralize if pluralize_table_names
- table_name
+ pluralize_table_names ? table_name.pluralize : table_name
end
```
@@ -2413,9 +2394,9 @@ or yields them in turn if a block is passed:
```html+erb
<% sample.in_groups_of(3) do |a, b, c| %>
<tr>
- <td><%=h a %></td>
- <td><%=h b %></td>
- <td><%=h c %></td>
+ <td><%= a %></td>
+ <td><%= b %></td>
+ <td><%= c %></td>
</tr>
<% end %>
```
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index b302ef76c6..fffa31927d 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -720,7 +720,31 @@ A good example of this is the `jquery-rails` gem which comes with Rails as the s
Making Your Library or Gem a Pre-Processor
------------------------------------------
-TODO: Registering gems on [Tilt](https://github.com/rtomayko/tilt) enabling Sprockets to find them.
+As Sprockets uses [Tilt](https://github.com/rtomayko/tilt) as a generic
+interface to different templating engines, your gem should just
+implement the Tilt template protocol. Normally, you would subclass
+`Tilt::Template` and reimplement `evaluate` method to return final
+output. Template source is stored at `@code`. Have a look at
+[`Tilt::Template`](https://github.com/rtomayko/tilt/blob/master/lib/tilt/template.rb)
+sources to learn more.
+
+```ruby
+module BangBang
+ class Template < ::Tilt::Template
+ # Adds a "!" to original template.
+ def evaluate(scope, locals, &block)
+ "#{@code}!"
+ end
+ end
+end
+```
+
+Now that you have a `Template` class, it's time to associate it with an
+extenstion for template files:
+
+```ruby
+Sprockets.register_engine '.bang', BangBang::Template
+```
Upgrading from Old Versions of Rails
------------------------------------
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index 773102400a..0228d463cf 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -30,101 +30,13 @@ config.action_controller.perform_caching = true
Page caching is a Rails mechanism which allows the request for a generated page to be fulfilled by the webserver (i.e. Apache or nginx), without ever having to go through the Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be applied to every situation (such as pages that need authentication) and since the webserver is literally just serving a file from the filesystem, cache expiration is an issue that needs to be dealt with.
-To enable page caching, you need to use the `caches_page` method.
-
-```ruby
-class ProductsController < ActionController
-
- caches_page :index
-
- def index
- @products = Product.all
- end
-end
-```
-
-Let's say you have a controller called `ProductsController` and an `index` action that lists all the products. The first time anyone requests `/products`, Rails will generate a file called `products.html` and the webserver will then look for that file before it passes the next request for `/products` to your Rails application.
-
-By default, the page cache directory is set to `Rails.public_path` (which is usually set to the `public` folder) and this can be configured by changing the configuration setting `config.action_controller.page_cache_directory`. Changing the default from `public` helps avoid naming conflicts, since you may want to put other static html in `public`, but changing this will require web server reconfiguration to let the web server know where to serve the cached files from.
-
-The Page Caching mechanism will automatically add a `.html` extension to requests for pages that do not have an extension to make it easy for the webserver to find those pages and this can be configured by changing the configuration setting `config.action_controller.default_static_extension`.
-
-In order to expire this page when a new product is added we could extend our example controller like this:
-
-```ruby
-class ProductsController < ActionController
-
- caches_page :index
-
- def index
- @products = Product.all
- end
-
- def create
- expire_page action: :index
- end
-
-end
-```
-
-By default, page caching automatically gzips files (for example, to `products.html.gz` if user requests `/products`) to reduce the size of data transmitted (web servers are typically configured to use a moderate compression ratio as a compromise, but since precompilation happens once, compression ratio is maximum).
-
-Nginx is able to serve compressed content directly from disk by enabling `gzip_static`:
-
-```nginx
-location / {
- gzip_static on; # to serve pre-gzipped version
-}
-```
-
-You can disable gzipping by setting `:gzip` option to false (for example, if action returns image):
-
-```ruby
-caches_page :image, gzip: false
-```
-
-Or, you can set custom gzip compression level (level names are taken from `Zlib` constants):
-
-```ruby
-caches_page :image, gzip: :best_speed
-```
-
-NOTE: Page caching ignores all parameters. For example `/products?page=1` will be written out to the filesystem as `products.html` with no reference to the `page` parameter. Thus, if someone requests `/products?page=2` later, they will get the cached first page. A workaround for this limitation is to include the parameters in the page's path, e.g. `/products/page/1`.
-
-INFO: Page caching runs in an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job.
+INFO: Page Caching has been removed from Rails 4. See the [actionpack-page_caching gem](https://github.com/rails/actionpack-page_caching)
### Action Caching
Page Caching cannot be used for actions that have before filters - for example, pages that require authentication. This is where Action Caching comes in. Action Caching works like Page Caching except the incoming web request hits the Rails stack so that before filters can be run on it before the cache is served. This allows authentication and other restrictions to be run while still serving the result of the output from a cached copy.
-Clearing the cache works in a similar way to Page Caching, except you use `expire_action` instead of `expire_page`.
-
-Let's say you only wanted authenticated users to call actions on `ProductsController`.
-
-```ruby
-class ProductsController < ActionController
-
- before_action :authenticate
- caches_action :index
-
- def index
- @products = Product.all
- end
-
- def create
- expire_action action: :index
- end
-
-end
-```
-
-You can also use `:if` (or `:unless`) to pass a Proc that specifies when the action should be cached. Also, you can use `layout: false` to cache without layout so that dynamic information in the layout such as logged in user info or the number of items in the cart can be left uncached. This feature is available as of Rails 2.2.
-
-You can modify the default action cache path by passing a `:cache_path` option. This will be passed directly to `ActionCachePath.path_for`. This is handy for actions with multiple possible routes that should be cached differently. If a block is given, it is called with the current controller instance.
-
-Finally, if you are using memcached or Ehcache, you can also pass `:expires_in`. In fact, all parameters not used by `caches_action` are sent to the underlying cache store.
-
-INFO: Action caching runs in an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job.
+INFO: Action Caching has been removed from Rails 4. See the [actionpack-action_caching gem](https://github.com/rails/actionpack-action_caching)
### Fragment Caching
@@ -196,10 +108,6 @@ class ProductsController < ActionController
end
```
-The second time the same query is run against the database, it's not actually going to hit the database. The first time the result is returned from the query it is stored in the query cache (in memory) and the second time it's pulled from memory.
-
-However, it's important to note that query caches are created at the start of an action and destroyed at the end of that action and thus persist only for the duration of the action. If you'd like to store query results in a more persistent fashion, you can in Rails by using low level caching.
-
Cache Stores
------------
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index fb15790d90..2790a4740a 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -225,7 +225,8 @@ $ rails generate scaffold HighScore game:string score:integer
invoke test_unit
create test/models/high_score_test.rb
create test/fixtures/high_scores.yml
- route resources :high_scores
+ invoke resource_route
+ route resources :high_scores
invoke scaffold_controller
create app/controllers/high_scores_controller.rb
invoke erb
@@ -354,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)
@@ -441,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'
@@ -563,14 +564,20 @@ We had to create the **gitapp** directory and initialize an empty git repository
$ cat config/database.yml
# PostgreSQL. Versions 8.2 and up are supported.
#
-# Install the ruby-postgres driver:
-# gem install ruby-postgres
-# On Mac OS X:
-# gem install ruby-postgres -- --include=/usr/local/pgsql
+# Install the pg driver:
+# gem install pg
+# On OS X with Homebrew:
+# gem install pg -- --with-pg-config=/usr/local/bin/pg_config
+# On OS X with MacPorts:
+# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
# On Windows:
-# gem install ruby-postgres
+# gem install pg
# Choose the win32 build.
# Install PostgreSQL and put its /bin directory on your path.
+#
+# Configure Using Gemfile
+# gem 'pg'
+#
development:
adapter: postgresql
encoding: unicode
@@ -585,28 +592,3 @@ development:
It also generated some lines in our database.yml configuration corresponding to our choice of PostgreSQL for database.
NOTE. The only catch with using the SCM options is that you have to make your application's directory first, then initialize your SCM, then you can run the `rails new` command to generate the basis of your app.
-
-### `server` with Different Backends
-
-Many people have created a large number of different web servers in Ruby, and many of them can be used to run Rails. Since version 2.3, Rails uses Rack to serve its webpages, which means that any webserver that implements a Rack handler can be used. This includes WEBrick, Mongrel, Thin, and Phusion Passenger (to name a few!).
-
-NOTE: For more details on the Rack integration, see [Rails on Rack](rails_on_rack.html).
-
-To use a different server, just install its gem, then use its name for the first parameter to `rails server`:
-
-```bash
-$ sudo gem install mongrel
-Building native extensions. This could take a while...
-Building native extensions. This could take a while...
-Successfully installed gem_plugin-0.2.3
-Successfully installed fastthread-1.0.1
-Successfully installed cgi_multipart_eof_fix-2.5.0
-Successfully installed mongrel-1.1.5
-...
-...
-Installing RDoc documentation for mongrel-1.1.5...
-$ rails server mongrel
-=> Booting Mongrel (use 'rails server webrick' to force WEBrick)
-=> Rails 3.1.0 application starting on http://0.0.0.0:3000
-...
-```
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 5fe8e2fba6..fc1d0d7530 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -177,7 +177,6 @@ The full set of methods that can be used in this block are as follows:
* `javascripts` turns on the hook for JavaScript files in generators. Used in Rails for when the `scaffold` generator is run. Defaults to `true`.
* `javascript_engine` configures the engine to be used (for eg. coffee) when generating assets. Defaults to `nil`.
* `orm` defines which orm to use. Defaults to `false` and will use Active Record by default.
-* `performance_tool` defines which performance tool to use. Defaults to `nil`.
* `resource_controller` defines which generator to use for generating a controller when using `rails generate resource`. Defaults to `:controller`.
* `scaffold_controller` different from `resource_controller`, defines which generator to use for generating a _scaffolded_ controller when using `rails generate scaffold`. Defaults to `:scaffold_controller`.
* `stylesheets` turns on the hook for stylesheets in generators. Used in Rails for when the `scaffold` generator is run, but this hook can be used in other generates as well. Defaults to `true`.
@@ -304,6 +303,8 @@ 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.
+
### Configuring Action Dispatch
* `config.action_dispatch.session_store` sets the name of the store for session data. The default is `:cookie_store`; other valid options include `:active_record_store`, `:mem_cache_store` or the name of your own custom class.
@@ -551,6 +552,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
--------------------------
@@ -583,7 +593,7 @@ Rails has 5 initialization events which can be hooked into (listed in the order
* `to_prepare`: Run after the initializers are run for all Railties (including the application itself), but before eager loading and the middleware stack is built. More importantly, will run upon every request in `development`, but only once (during boot-up) in `production` and `test`.
-* `before_eager_load`: This is run directly before eager loading occurs, which is the default behaviour for the `production` environment and not for the `development` environment.
+* `before_eager_load`: This is run directly before eager loading occurs, which is the default behavior for the `production` environment and not for the `development` environment.
* `after_initialize`: Run directly after the initialization of the application, but before the application initializers are run.
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index 7c5a472971..baef620c6c 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -216,6 +216,7 @@ Rails follows a simple set of coding style conventions:
* Use `MyClass.my_method(my_arg)` not `my_method( my_arg )` or `my_method my_arg`.
* Use `a = b` and not `a=b`.
* Use assert_not methods instead of refute.
+* Prefer `method { do_stuff }` instead of `method{do_stuff}` for single-line blocks.
* Follow the conventions in the source you see used already.
The above are guidelines — please use your best judgment in using them.
diff --git a/guides/source/credits.html.erb b/guides/source/credits.html.erb
index e25168d58d..ff76fa2b85 100644
--- a/guides/source/credits.html.erb
+++ b/guides/source/credits.html.erb
@@ -28,7 +28,7 @@ Ruby on Rails Guides: Credits
<h3 class="section">Rails Guides Authors</h3>
<%= author('Ryan Bigg', 'radar', 'radar.png') do %>
-Ryan Bigg works as a consultant at <a href="http://rubyx.com">RubyX</a> and has been working with Rails since 2006. He's co-authoring a book called <a href="http://manning.com/katz">Rails 3 in Action</a> and he's written many gems which can be seen on <a href="http://github.com/radar">his GitHub page</a> and he also tweets prolifically as <a href="http://twitter.com/ryanbigg">@ryanbigg</a>.
+Ryan Bigg works as a consultant at <a href="http://rubyx.com">RubyX</a> and has been working with Rails since 2006. He's co-authoring a book called <a href="http://manning.com/katz">Rails 3 in Action</a> and he's written many gems which can be seen on <a href="https://github.com/radar">his GitHub page</a> and he also tweets prolifically as <a href="http://twitter.com/ryanbigg">@ryanbigg</a>.
<% end %>
<%= author('Oscar Del Ben', 'oscardelben', 'oscardelben.jpg') do %>
diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md
index 2e90e8728c..5531dee343 100644
--- a/guides/source/debugging_rails_applications.md
+++ b/guides/source/debugging_rails_applications.md
@@ -29,7 +29,7 @@ The `debug` helper will return a \<pre>-tag that renders the object using the YA
<%= debug @post %>
<p>
<b>Title:</b>
- <%=h @post.title %>
+ <%= @post.title %>
</p>
```
@@ -58,7 +58,7 @@ Displaying an instance variable, or any other object or method, in YAML format c
<%= simple_format @post.to_yaml %>
<p>
<b>Title:</b>
- <%=h @post.title %>
+ <%= @post.title %>
</p>
```
@@ -88,7 +88,7 @@ Another useful method for displaying object values is `inspect`, especially when
<%= [1, 2, 3, 4, 5].inspect %>
<p>
<b>Title:</b>
- <%=h @post.title %>
+ <%= @post.title %>
</p>
```
@@ -664,7 +664,7 @@ References
----------
* [ruby-debug Homepage](http://bashdb.sourceforge.net/ruby-debug/home-page.html)
-* [debugger Homepage](http://github.com/cldwalker/debugger)
+* [debugger Homepage](https://github.com/cldwalker/debugger)
* [Article: Debugging a Rails application with ruby-debug](http://www.sitepoint.com/article/debug-rails-app-ruby-debug/)
* [ruby-debug Basics screencast](http://brian.maybeyoureinsane.net/blog/2007/05/07/ruby-debug-basics-screencast/)
* [Ryan Bates' debugging ruby (revised) screencast](http://railscasts.com/episodes/54-debugging-ruby-revised)
diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml
index e779407fab..c73bbeb90d 100644
--- a/guides/source/documents.yaml
+++ b/guides/source/documents.yaml
@@ -84,10 +84,6 @@
url: debugging_rails_applications.html
description: This guide describes how to debug Rails applications. It covers the different ways of achieving this and how to understand what is happening "behind the scenes" of your code.
-
- name: Performance Testing Rails Applications
- url: performance_testing.html
- description: This guide covers the various ways of performance testing a Ruby on Rails application.
- -
name: Configuring Rails Applications
url: configuring.html
description: This guide covers the basic configuration settings for a Rails application.
@@ -106,7 +102,6 @@
description: This guide documents the asset pipeline.
-
name: Working with JavaScript in Rails
- work_in_progress: true
url: working_with_javascript_in_rails.html
description: This guide covers the built-in Ajax/JavaScript functionality of Rails.
-
diff --git a/guides/source/engines.md b/guides/source/engines.md
index 116a7e67cd..f35233993c 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -16,9 +16,9 @@ After reading this guide, you will know:
What are engines?
-----------------
-Engines can be considered miniature applications that provide functionality to their host applications. A Rails application is actually just a "supercharged" engine, with the `Rails::Application` class inheriting a lot of its behaviour from `Rails::Engine`.
+Engines can be considered miniature applications that provide functionality to their host applications. A Rails application is actually just a "supercharged" engine, with the `Rails::Application` class inheriting a lot of its behavior from `Rails::Engine`.
-Therefore, engines and applications can be thought of almost the same thing, just with very minor differences, as you'll see throughout this guide. Engines and applications also share a common structure.
+Therefore, engines and applications can be thought of almost the same thing, just with subtle differences, as you'll see throughout this guide. Engines and applications also share a common structure.
Engines are also closely related to plugins where the two share a common `lib` directory structure and are both generated using the `rails plugin new` generator. The difference being that an engine is considered a "full plugin" by Rails as indicated by the `--full` option that's passed to the generator command, but this guide will refer to them simply as "engines" throughout. An engine **can** be a plugin, and a plugin **can** be an engine.
@@ -149,9 +149,9 @@ Lastly, the `app/views` directory contains a `layouts` folder which contains a f
If you don't want to force a layout on to users of the engine, then you can delete this file and reference a different layout in the controllers of your engine.
-#### `script` directory
+#### `bin` directory
-This directory contains one file, `script/rails`, which enables you to use the `rails` sub-commands and generators just like you would within an application. This means that you will very easily be able to generate new controllers and models for this engine by running commands like this:
+This directory contains one file, `bin/rails`, which enables you to use the `rails` sub-commands and generators just like you would within an application. This means that you will very easily be able to generate new controllers and models for this engine by running commands like this:
```bash
rails g model
@@ -171,7 +171,7 @@ end
This line mounts the engine at the path `/blorgh`, which will make it accessible through the application only at that path.
-Also in the test directory is the `test/integration` directory, where integration tests for the engine should be placed. Other directories can be created in the `test` directory also. For example, you may wish to create a `test/models` directory for your models tests.
+In the test directory there is the `test/integration` directory, where integration tests for the engine should be placed. Other directories can be created in the `test` directory as well. For example, you may wish to create a `test/models` directory for your models tests.
Providing engine functionality
------------------------------
@@ -232,7 +232,8 @@ Blorgh::Engine.routes.draw do
end
```
-Note here that the routes are drawn upon the `Blorgh::Engine` object rather than the `YourApp::Application` class. This is so that the engine routes are confined to the engine itself and can be mounted at a specific point as shown in the [test directory](#test-directory) section. This is also what causes the engine's routes to be isolated from those routes that are within the application. This is discussed further in the [Routes](#routes) section of this guide.
+Note here that the routes are drawn upon the `Blorgh::Engine` object rather than the `YourApp::Application` class. This is so that the engine routes are confined to the engine itself and can be mounted at a specific point as shown in the [test directory](#test-directory) section. It also causes the engine's routes to be isolated from those routes that are within the application. The [Routes](#routes) section of
+this guide describes it in details.
Next, the `scaffold_controller` generator is invoked, generating a controller called `Blorgh::PostsController` (at `app/controllers/blorgh/posts_controller.rb`) and its related views at `app/views/blorgh/posts`. This generator also generates a test for the controller (`test/controllers/blorgh/posts_controller_test.rb`) and a helper (`app/helpers/blorgh/posts_controller.rb`).
@@ -258,7 +259,7 @@ module Blorgh
end
```
-This helps prevent conflicts with any other engine or application that may have a post resource also.
+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.
@@ -287,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 has the ability 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 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.
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.
@@ -531,7 +532,7 @@ before_save :set_author
private
def set_author
- self.author = User.find_or_create_by_name(author_name)
+ self.author = User.find_or_create_by(name: author_name)
end
```
@@ -630,7 +631,7 @@ belongs_to :author, class_name: Blorgh.user_class
The `set_author` method also located in this class should also use this class:
```ruby
-self.author = Blorgh.user_class.constantize.find_or_create_by_name(author_name)
+self.author = Blorgh.user_class.constantize.find_or_create_by(name: author_name)
```
To save having to call `constantize` on the `user_class` result all the time, you could instead just override the `user_class` getter method inside the `Blorgh` module in the `lib/blorgh.rb` file to always call `constantize` on the saved value before returning the result:
@@ -644,10 +645,10 @@ end
This would then turn the above code for `set_author` into this:
```ruby
-self.author = Blorgh.user_class.find_or_create_by_name(author_name)
+self.author = Blorgh.user_class.find_or_create_by(name: author_name)
```
-Resulting in something a little shorter, and more implicit in its behaviour. The `user_class` method should always return a `Class` object.
+Resulting in something a little shorter, and more implicit in its behavior. The `user_class` method should always return a `Class` object.
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.
@@ -661,7 +662,7 @@ WARNING: It's very important here to use the `String` version of the class, rath
Go ahead and try to create a new post. You will see that it works exactly in the same way as before, except this time the engine is using the configuration setting in `config/initializers/blorgh.rb` to learn what the class is.
-There are now no strict dependencies on what the class is, only what the API for the class must be. The engine simply requires this class to define a `find_or_create_by_name` method which returns an object of that class to be associated with a post when it's created. This object, of course, should have some sort of identifier by which it can be referenced.
+There are now no strict dependencies on what the class is, only what the API for the class must be. The engine simply requires this class to define a `find_or_create_by` method which returns an object of that class to be associated with a post when it's created. This object, of course, should have some sort of identifier by which it can be referenced.
#### General engine configuration
@@ -800,7 +801,7 @@ module Blorgh::Concerns::Models::Post
private
def set_author
- self.author = User.find_or_create_by_name(author_name)
+ self.author = User.find_or_create_by(name: author_name)
end
end
@@ -914,9 +915,10 @@ For more information, read the [Asset Pipeline guide](http://guides.rubyonrails.
### Other gem dependencies
-Gem dependencies inside an engine should be specified inside the `.gemspec` file at the root of the engine. The reason for this is because the engine may
+Gem dependencies inside an engine should be specified inside the
+`.gemspec` file at the root of the engine. The reason is that the engine may
be installed as a gem. If dependencies were to be specified inside the `Gemfile`,
-these would not be recognised by a traditional gem install and so they would not
+these would not be recognized by a traditional gem install and so they would not
be installed, causing the engine to malfunction.
To specify a dependency that should be installed with the engine during a
diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md
index b7145c46dc..b8681d493a 100644
--- a/guides/source/form_helpers.md
+++ b/guides/source/form_helpers.md
@@ -238,7 +238,7 @@ end
The corresponding view `app/views/articles/new.html.erb` using `form_for` looks like this:
```erb
-<%= form_for @article, url: {action: "create"}, html => {class: "nifty_form"} do |f| %>
+<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body, size: "60x12" %>
<%= f.submit "Create" %>
@@ -428,7 +428,7 @@ WARNING: when `:inlude_blank` or `:prompt:` are not present, `:include_blank` is
You can add arbitrary attributes to the options using hashes:
```html+erb
-<%= options_for_select([['Lisbon', 1, 'data-size': '2.8 million'], ['Madrid', 2, 'data-size': '3.2 million']], 2) %>
+<%= options_for_select([['Lisbon', 1, {'data-size' => '2.8 million'}], ['Madrid', 2, {'data-size' => '3.2 million'}]], 2) %>
output:
@@ -497,7 +497,7 @@ To leverage time zone support in Rails, you have to ask your users what time zon
There is also `time_zone_options_for_select` helper for a more manual (therefore more customizable) way of doing this. Read the API documentation to learn about the possible arguments for these two methods.
-Rails _used_ to have a `country_select` helper for choosing countries, but this has been extracted to the [country_select plugin](https://github.com/chrislerum/country_select). When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from Rails).
+Rails _used_ to have a `country_select` helper for choosing countries, but this has been extracted to the [country_select plugin](https://github.com/stefanpenner/country_select). When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from Rails).
Using Date and Time Form Helpers
--------------------------------
diff --git a/guides/source/generators.md b/guides/source/generators.md
index 62de5a70bb..8b91dfc5a5 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -176,7 +176,8 @@ $ rails generate scaffold User name:string
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
- route resources :users
+ invoke resource_route
+ route resources :users
invoke scaffold_controller
create app/controllers/users_controller.rb
invoke erb
@@ -192,8 +193,13 @@ $ rails generate scaffold User name:string
create app/helpers/users_helper.rb
invoke test_unit
create test/helpers/users_helper_test.rb
- invoke stylesheets
- create app/assets/stylesheets/scaffold.css
+ invoke assets
+ invoke coffee
+ create app/assets/javascripts/users.js.coffee
+ invoke scss
+ create app/assets/stylesheets/users.css.scss
+ invoke scss
+ create app/assets/stylesheets/scaffolds.css.scss
```
Looking at this output, it's easy to understand how generators work in Rails 3.0 and above. The scaffold generator doesn't actually generate anything, it just invokes others to do the work. This allows us to add/replace/remove any of those invocations. For instance, the scaffold generator invokes the scaffold_controller generator, which invokes erb, test_unit and helper generators. Since each generator has a single responsibility, they are easy to reuse, avoiding code duplication.
@@ -350,6 +356,7 @@ $ rails generate scaffold Comment body:text
invoke shoulda
create test/models/comment_test.rb
create test/fixtures/comments.yml
+ invoke resource_route
route resources :comments
invoke scaffold_controller
create app/controllers/comments_controller.rb
@@ -360,13 +367,16 @@ $ rails generate scaffold Comment body:text
create app/views/comments/show.html.erb
create app/views/comments/new.html.erb
create app/views/comments/_form.html.erb
- create app/views/layouts/comments.html.erb
invoke shoulda
create test/controllers/comments_controller_test.rb
invoke my_helper
create app/helpers/comments_helper.rb
invoke shoulda
create test/helpers/comments_helper_test.rb
+ invoke assets
+ invoke coffee
+ create app/assets/javascripts/comments.js.coffee
+ invoke scss
```
Fallbacks allow your generators to have a single responsibility, increasing code reuse and reducing the amount of duplication.
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index aa841d5867..7d86b3866a 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -45,7 +45,7 @@ code while accomplishing more than many other languages and frameworks.
Experienced Rails developers also report that it makes web application
development more fun.
-Rails is opinionated software. It makes the assumption that there is a "best"
+Rails is opinionated software. It makes the assumption that there is the "best"
way to do things, and it's designed to encourage that way - and in some cases to
discourage alternatives. If you learn "The Rails Way" you'll probably discover a
tremendous increase in productivity. If you persist in bringing old habits from
@@ -71,7 +71,9 @@ By following along with this guide, you'll create a Rails project called
(very) simple weblog. Before you can start building the application, you need to
make sure that you have Rails itself installed.
-TIP: The examples below use # and $ to denote superuser and regular user terminal prompts respectively in a UNIX-like OS. If you are using Windows, your prompt will look something like c:\source_code>
+TIP: The examples below use `#` and `$` to denote superuser and regular
+user terminal prompts respectively in a UNIX-like OS. If you are using
+Windows, your prompt will look something like `c:\source_code>`
### Installing Rails
@@ -132,17 +134,16 @@ application. Most of the work in this tutorial will happen in the `app/` folder,
| File/Folder | Purpose |
| ----------- | ------- |
|app/|Contains the controllers, models, views, helpers, mailers and assets for your application. You'll focus on this folder for the remainder of this guide.|
+|bin/|Contains the rails script that starts your app and can contain other scripts you use to deploy or run your application.|
|config/|Configure your application's runtime rules, routes, database, and more. This is covered in more detail in [Configuring Rails Applications](configuring.html)|
|config.ru|Rack configuration for Rack based servers used to start the application.|
|db/|Contains your current database schema, as well as the database migrations.|
-|doc/|In-depth documentation for your application.|
|Gemfile<br />Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see [the Bundler website](http://gembundler.com) |
|lib/|Extended modules for your application.|
|log/|Application log files.|
|public/|The only folder seen to the world as-is. Contains the static files and compiled assets.|
|Rakefile|This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing Rakefile, you should add your own tasks by adding files to the lib/tasks directory of your application.|
|README.rdoc|This is a brief instruction manual for your application. You should edit this file to tell others what your application does, how to set it up, and so on.|
-|script/|Contains the rails script that starts your app and can contain other scripts you use to deploy or run your application.|
|test/|Unit tests, fixtures, and other test apparatus. These are covered in [Testing Rails Applications](testing.html)|
|tmp/|Temporary files (like cache, pid and session files)|
|vendor/|A place for all third-party code. In a typical Rails application, this includes Ruby Gems and the Rails source code (if you optionally install it into your project).|
@@ -417,7 +418,7 @@ def create
end
```
-The `render` method here is taking a very simple hash with a key of `text` and value of `params[:post].inspect`. The `params` method is the object which represents the parameters (or fields) coming in from the form. The `params` method returns a `HashWithIndifferentAccess` object, which allows you to access the keys of the hash using either strings or symbols. In this situation, the only parameters that matter are the ones from the form.
+The `render` method here is taking a very simple hash with a key of `text` and value of `params[:post].inspect`. The `params` method is the object which represents the parameters (or fields) coming in from the form. The `params` method returns an `ActiveSupport::HashWithIndifferentAccess` object, which allows you to access the keys of the hash using either strings or symbols. In this situation, the only parameters that matter are the ones from the form.
If you re-submit the form one more time you'll now no longer get the missing template error. Instead, you'll see something that looks like the following:
@@ -830,7 +831,7 @@ it look as follows:
<h1>Editing post</h1>
<%= form_for :post, url: { action: :update, id: @post.id },
-method: :put do |f| %>
+method: :patch do |f| %>
<% if @post.errors.any? %>
<div id="errorExplanation">
<h2><%= pluralize(@post.errors.count, "error") %> prohibited
@@ -863,7 +864,7 @@ method: :put do |f| %>
This time we point the form to the `update` action, which is not defined yet
but will be very soon.
-The `method: :put` option tells Rails that we want this form to be
+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
**update** resources according to the REST protocol.
@@ -873,7 +874,7 @@ Next, we need to add the `update` action. The file
`config/routes.rb` will need just one more line:
```ruby
-put "posts/:id" => "posts#update"
+patch "posts/:id" => "posts#update"
```
And then create the `update` action in `app/controllers/posts_controller.rb`:
@@ -1051,7 +1052,7 @@ called `post_url` and `post_path` available to our application. These are
precisely the methods that the `form_for` needs when editing a post, and so now
you'll be able to update posts again.
-NOTE: The `:as` option is available on the `post`, `put`, `delete` and `match`
+NOTE: The `:as` option is available on the `post`, `patch`, `put`, `delete` and `match`
routing methods also.
### Deleting Posts
@@ -1145,7 +1146,7 @@ get "posts/new"
post "posts" => "posts#create"
get "posts/:id" => "posts#show", as: :post
get "posts/:id/edit" => "posts#edit"
-put "posts/:id" => "posts#update"
+patch "posts/:id" => "posts#update"
delete "posts/:id" => "posts#destroy"
```
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index 2e61bea5ea..69232d9bd4 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -897,7 +897,7 @@ The I18n API will catch all of these exceptions when they are thrown in the back
The reason for this is that during development you'd usually want your views to still render even though a translation is missing.
-In other contexts you might want to change this behaviour, though. E.g. the default exception handling does not allow to catch missing translations during automated tests easily. For this purpose a different exception handler can be specified. The specified exception handler must be a method on the I18n module or a class with `#call` method:
+In other contexts you might want to change this behavior, though. E.g. the default exception handling does not allow to catch missing translations during automated tests easily. For this purpose a different exception handler can be specified. The specified exception handler must be a method on the I18n module or a class with `#call` method:
```ruby
module I18n
@@ -927,7 +927,7 @@ else
end
```
-Another example where the default behaviour is less desirable is the Rails TranslationHelper which provides the method `#t` (as well as `#translate`). When a `MissingTranslationData` exception occurs in this context, the helper wraps the message into a span with the CSS class `translation_missing`.
+Another example where the default behavior is less desirable is the Rails TranslationHelper which provides the method `#t` (as well as `#translate`). When a `MissingTranslationData` exception occurs in this context, the helper wraps the message into a span with the CSS class `translation_missing`.
To do so, the helper forces `I18n#translate` to raise exceptions no matter what exception handler is defined by setting the `:raise` option:
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index 32df508f9c..8ba5fa4601 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -26,126 +26,16 @@ quickly.
Launch!
-------
-A Rails application is usually started with the command `rails server`.
+Now we finally boot and initialize the app. It all starts with your app's
+`bin/rails` executable. A Rails application is usually started by running
+`rails console` or `rails server`.
### `bin/rails`
-The actual `rails` command is kept in _bin/rails_:
-
-```ruby
-#!/usr/bin/env ruby
-
-if File.exists?(File.join(File.expand_path('../../..', __FILE__), '.git'))
- railties_path = File.expand_path('../../lib', __FILE__)
- $:.unshift(railties_path)
-end
-require "rails/cli"
-```
-
-This file will first attempt to push the `railties/lib` directory if
-present, and then requires `rails/cli`.
-
-### `railties/lib/rails/cli.rb`
-
-This file looks like this:
-
-```ruby
-require 'rbconfig'
-require 'rails/script_rails_loader'
-
-# If we are inside a Rails application this method performs an exec and thus
-# the rest of this script is not run.
-Rails::ScriptRailsLoader.exec_script_rails!
-
-require 'rails/ruby_version_check'
-Signal.trap("INT") { puts; exit(1) }
-
-if ARGV.first == 'plugin'
- ARGV.shift
- require 'rails/commands/plugin_new'
-else
- require 'rails/commands/application'
-end
-```
-
-The `rbconfig` file from the Ruby standard library provides us with the `RbConfig` class which contains detailed information about the Ruby environment, including how Ruby was compiled. We can see this in use in `railties/lib/rails/script_rails_loader`.
-
-```ruby
-require 'pathname'
-
-module Rails
- module ScriptRailsLoader
- RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"]
- SCRIPT_RAILS = File.join('script', 'rails')
- ...
-
- end
-end
-```
-
-The `rails/script_rails_loader` file uses `RbConfig::Config` to obtain the `bin_dir` and `ruby_install_name` values for the configuration which together form the path to the Ruby interpreter. The `RbConfig::CONFIG["EXEEXT"]` will suffix this path with ".exe" if the script is running on Windows. This constant is used later on in `exec_script_rails!`. As for the `SCRIPT_RAILS` constant, we'll see that when we get to the `in_rails_application?` method.
-
-Back in `rails/cli`, the next line is this:
-
-```ruby
-Rails::ScriptRailsLoader.exec_script_rails!
-```
-
-This method is defined in `rails/script_rails_loader`:
-
-```ruby
-def self.exec_script_rails!
- cwd = Dir.pwd
- return unless in_rails_application? || in_rails_application_subdirectory?
- exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application?
- Dir.chdir("..") do
- # Recurse in a chdir block: if the search fails we want to be sure
- # the application is generated in the original working directory.
- exec_script_rails! unless cwd == Dir.pwd
- end
-rescue SystemCallError
- # could not chdir, no problem just return
-end
-```
-
-This method will first check if the current working directory (`cwd`) is a Rails application or a subdirectory of one. This is determined by the `in_rails_application?` method:
-
-```ruby
-def self.in_rails_application?
- File.exists?(SCRIPT_RAILS)
-end
-```
-
-The `SCRIPT_RAILS` constant defined earlier is used here, with `File.exists?` checking for its presence in the current directory. If this method returns `false` then `in_rails_application_subdirectory?` will be used:
-
-```ruby
-def self.in_rails_application_subdirectory?(path = Pathname.new(Dir.pwd))
- File.exists?(File.join(path, SCRIPT_RAILS)) || !path.root? && in_rails_application_subdirectory?(path.parent)
-end
-```
-
-This climbs the directory tree until it reaches a path which contains a `script/rails` file. If a directory containing this file is reached then this line will run:
-
-```ruby
-exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application?
-```
-
-This is effectively the same as running `ruby script/rails [arguments]`, where `[arguments]` at this point in time is simply "server".
-
-Rails Initialization
---------------------
-
-Only now we finally start the real initialization process, beginning
-with `script/rails`.
-
-TIP: If you execute `script/rails` directly from your Rails app you will
-skip executing all the code that we've just described.
-
-### `script/rails`
-
This file is as follows:
```ruby
+#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application', __FILE__)
require File.expand_path('../../config/boot', __FILE__)
require 'rails/commands'
@@ -227,18 +117,18 @@ If we used `s` rather than `server`, Rails will use the `aliases` defined in the
```ruby
when 'server'
# Change to the application's path if there is no config.ru file in current dir.
- # This allows us to run script/rails server from other directories, but still get
+ # This allows us to run `rails server` from other directories, but still get
# the main config.ru and properly set the tmp directory.
Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru"))
require 'rails/commands/server'
- Rails::Server.new.tap { |server|
+ Rails::Server.new.tap do |server|
# We need to require application after the server sets environment,
# otherwise the --environment option given to the server won't propagate.
require APP_PATH
Dir.chdir(Rails.application.root)
server.start
- }
+ end
```
This file will change into the root of the directory (a path two directories back from `APP_PATH` which points at `config/application.rb`), but only if the `config.ru` file isn't found. This then requires `rails/commands/server` which sets up the `Rails::Server` class.
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index fa303745b8..7c26512046 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -97,7 +97,7 @@ NOTE: The actual rendering is done by subclasses of `ActionView::TemplateHandler
### Using `render`
-In most cases, the `ActionController::Base#render` method does the heavy lifting of rendering your application's content for use by a browser. There are a variety of ways to customize the behaviour of `render`. You can render the default view for a Rails template, or a specific template, or a file, or inline code, or nothing at all. You can render text, JSON, or XML. You can specify the content type or HTTP status of the rendered response as well.
+In most cases, the `ActionController::Base#render` method does the heavy lifting of rendering your application's content for use by a browser. There are a variety of ways to customize the behavior of `render`. You can render the default view for a Rails template, or a specific template, or a file, or inline code, or nothing at all. You can render text, JSON, or XML. You can specify the content type or HTTP status of the rendered response as well.
TIP: If you want to see the exact results of a call to `render` without needing to inspect it in a browser, you can call `render_to_string`. This method takes exactly the same options as `render`, but it returns a string instead of sending a response back to the browser.
diff --git a/guides/source/nested_model_forms.md b/guides/source/nested_model_forms.md
index 2b46a9d51e..93d8e8dfcd 100644
--- a/guides/source/nested_model_forms.md
+++ b/guides/source/nested_model_forms.md
@@ -56,7 +56,7 @@ end
### Custom model
-As you might have inflected from this explanation, you _don’t_ necessarily need an ActiveRecord::Base model to use this functionality. The following examples are sufficient to enable the nested model form behaviour:
+As you might have inflected from this explanation, you _don’t_ necessarily need an ActiveRecord::Base model to use this functionality. The following examples are sufficient to enable the nested model form behavior:
#### Single associated object
diff --git a/guides/source/performance_testing.md b/guides/source/performance_testing.md
deleted file mode 100644
index ee0059623c..0000000000
--- a/guides/source/performance_testing.md
+++ /dev/null
@@ -1,686 +0,0 @@
-Performance Testing Rails Applications
-======================================
-
-This guide covers the various ways of performance testing a Ruby on Rails
-application.
-
-After reading this guide, you will know:
-
-* The various types of benchmarking and profiling metrics.
-* How to generate performance and benchmarking tests.
-* How to install and use a GC-patched Ruby binary to measure memory usage and object
- allocation.
-* The benchmarking information provided by Rails inside the log files.
-* Various tools facilitating benchmarking and profiling.
-
-Performance testing is an integral part of the development cycle. It is very
-important that you don't make your end users wait for too long before the page
-is completely loaded. Ensuring a pleasant browsing experience for end users and
-cutting the cost of unnecessary hardware is important for any non-trivial web
-application.
-
---------------------------------------------------------------------------------
-
-Performance Test Cases
-----------------------
-
-Rails performance tests are a special type of integration tests, designed for
-benchmarking and profiling the test code. With performance tests, you can
-determine where your application's memory or speed problems are coming from,
-and get a more in-depth picture of those problems.
-
-In a freshly generated Rails application, `test/performance/browsing_test.rb`
-contains an example of a performance test:
-
-```ruby
-require 'test_helper'
-require 'rails/performance_test_help'
-
-class BrowsingTest < ActionDispatch::PerformanceTest
- # Refer to the documentation for all available options
- # self.profile_options = { runs: 5, metrics: [:wall_time, :memory],
- # output: 'tmp/performance', formats: [:flat] }
-
- test "homepage" do
- get '/'
- end
-end
-```
-
-This example is a simple performance test case for profiling a GET request to
-the application's homepage.
-
-### Generating Performance Tests
-
-Rails provides a generator called `performance_test` for creating new
-performance tests:
-
-```bash
-$ rails generate performance_test homepage
-```
-
-This generates `homepage_test.rb` in the `test/performance` directory:
-
-```ruby
-require 'test_helper'
-require 'rails/performance_test_help'
-
-class HomepageTest < ActionDispatch::PerformanceTest
- # Refer to the documentation for all available options
- # self.profile_options = { runs: 5, metrics: [:wall_time, :memory],
- # output: 'tmp/performance', formats: [:flat] }
-
- test "homepage" do
- get '/'
- end
-end
-```
-
-### Examples
-
-Let's assume your application has the following controller and model:
-
-```ruby
-# routes.rb
-root to: 'home#dashboard'
-resources :posts
-
-# home_controller.rb
-class HomeController < ApplicationController
- def dashboard
- @users = User.last_ten.includes(:avatars)
- @posts = Post.all_today
- end
-end
-
-# posts_controller.rb
-class PostsController < ApplicationController
- def create
- @post = Post.create(params[:post])
- redirect_to(@post)
- end
-end
-
-# post.rb
-class Post < ActiveRecord::Base
- before_save :recalculate_costly_stats
-
- def slow_method
- # I fire gallzilion queries sleeping all around
- end
-
- private
-
- def recalculate_costly_stats
- # CPU heavy calculations
- end
-end
-```
-
-#### Controller Example
-
-Because performance tests are a special kind of integration test, you can use
-the `get` and `post` methods in them.
-
-Here's the performance test for `HomeController#dashboard` and
-`PostsController#create`:
-
-```ruby
-require 'test_helper'
-require 'rails/performance_test_help'
-
-class PostPerformanceTest < ActionDispatch::PerformanceTest
- def setup
- # Application requires logged-in user
- login_as(:lifo)
- end
-
- test "homepage" do
- get '/dashboard'
- end
-
- test "creating new post" do
- post '/posts', post: { body: 'lifo is fooling you' }
- end
-end
-```
-
-You can find more details about the `get` and `post` methods in the
-[Testing Rails Applications](testing.html) guide.
-
-#### Model Example
-
-Even though the performance tests are integration tests and hence closer to
-the request/response cycle by nature, you can still performance test pure model
-code.
-
-Performance test for `Post` model:
-
-```ruby
-require 'test_helper'
-require 'rails/performance_test_help'
-
-class PostModelTest < ActionDispatch::PerformanceTest
- test "creation" do
- Post.create body: 'still fooling you', cost: '100'
- end
-
- test "slow method" do
- # Using posts(:awesome) fixture
- posts(:awesome).slow_method
- end
-end
-```
-
-### Modes
-
-Performance tests can be run in two modes: Benchmarking and Profiling.
-
-#### Benchmarking
-
-Benchmarking makes it easy to quickly gather a few metrics about each test run.
-By default, each test case is run **4 times** in benchmarking mode.
-
-To run performance tests in benchmarking mode:
-
-```bash
-$ rake test:benchmark
-```
-
-#### Profiling
-
-Profiling allows you to make an in-depth analysis of each of your tests by using
-an external profiler. Depending on your Ruby interpreter, this profiler can be
-native (Rubinius, JRuby) or not (MRI, which uses RubyProf). By default, each
-test case is run **once** in profiling mode.
-
-To run performance tests in profiling mode:
-
-```bash
-$ rake test:profile
-```
-
-### Metrics
-
-Benchmarking and profiling run performance tests and give you multiple metrics.
-The availability of each metric is determined by the interpreter being used—none
-of them support all metrics—and by the mode in use. A brief description of each
-metric and their availability across interpreters/modes is given below.
-
-#### Wall Time
-
-Wall time measures the real world time elapsed during the test run. It is
-affected by any other processes concurrently running on the system.
-
-#### Process Time
-
-Process time measures the time taken by the process. It is unaffected by any
-other processes running concurrently on the same system. Hence, process time
-is likely to be constant for any given performance test, irrespective of the
-machine load.
-
-#### CPU Time
-
-Similar to process time, but leverages the more accurate CPU clock counter
-available on the Pentium and PowerPC platforms.
-
-#### User Time
-
-User time measures the amount of time the CPU spent in user-mode, i.e. within
-the process. This is not affected by other processes and by the time it possibly
-spends blocked.
-
-#### Memory
-
-Memory measures the amount of memory used for the performance test case.
-
-#### Objects
-
-Objects measures the number of objects allocated for the performance test case.
-
-#### GC Runs
-
-GC Runs measures the number of times GC was invoked for the performance test case.
-
-#### GC Time
-
-GC Time measures the amount of time spent in GC for the performance test case.
-
-#### Metric Availability
-
-##### Benchmarking
-
-| Interpreter | Wall Time | Process Time | CPU Time | User Time | Memory | Objects | GC Runs | GC Time |
-| ------------ | --------- | ------------ | -------- | --------- | ------ | ------- | ------- | ------- |
-| **MRI** | yes | yes | yes | no | yes | yes | yes | yes |
-| **REE** | yes | yes | yes | no | yes | yes | yes | yes |
-| **Rubinius** | yes | no | no | no | yes | yes | yes | yes |
-| **JRuby** | yes | no | no | yes | yes | yes | yes | yes |
-
-##### Profiling
-
-| Interpreter | Wall Time | Process Time | CPU Time | User Time | Memory | Objects | GC Runs | GC Time |
-| ------------ | --------- | ------------ | -------- | --------- | ------ | ------- | ------- | ------- |
-| **MRI** | yes | yes | no | no | yes | yes | yes | yes |
-| **REE** | yes | yes | no | no | yes | yes | yes | yes |
-| **Rubinius** | yes | no | no | no | no | no | no | no |
-| **JRuby** | yes | no | no | no | no | no | no | no |
-
-NOTE: To profile under JRuby you'll need to run `export JRUBY_OPTS="-Xlaunch.inproc=false --profile.api"`
-**before** the performance tests.
-
-### Understanding the Output
-
-Performance tests generate different outputs inside `tmp/performance` directory
-depending on their mode and metric.
-
-#### Benchmarking
-
-In benchmarking mode, performance tests generate two types of outputs.
-
-##### Command Line
-
-This is the primary form of output in benchmarking mode. Example:
-
-```bash
-BrowsingTest#test_homepage (31 ms warmup)
- wall_time: 6 ms
- memory: 437.27 KB
- objects: 5,514
- gc_runs: 0
- gc_time: 19 ms
-```
-
-##### CSV Files
-
-Performance test results are also appended to `.csv` files inside `tmp/performance`.
-For example, running the default `BrowsingTest#test_homepage` will generate
-following five files:
-
-* BrowsingTest#test_homepage_gc_runs.csv
-* BrowsingTest#test_homepage_gc_time.csv
-* BrowsingTest#test_homepage_memory.csv
-* BrowsingTest#test_homepage_objects.csv
-* BrowsingTest#test_homepage_wall_time.csv
-
-As the results are appended to these files each time the performance tests are
-run in benchmarking mode, you can collect data over a period of time. This can
-be very helpful in analyzing the effects of code changes.
-
-Sample output of `BrowsingTest#test_homepage_wall_time.csv`:
-
-```bash
-measurement,created_at,app,rails,ruby,platform
-0.00738224999999992,2009-01-08T03:40:29Z,,3.0.0,ruby-1.8.7.249,x86_64-linux
-0.00755874999999984,2009-01-08T03:46:18Z,,3.0.0,ruby-1.8.7.249,x86_64-linux
-0.00762099999999993,2009-01-08T03:49:25Z,,3.0.0,ruby-1.8.7.249,x86_64-linux
-0.00603075000000008,2009-01-08T04:03:29Z,,3.0.0,ruby-1.8.7.249,x86_64-linux
-0.00619899999999995,2009-01-08T04:03:53Z,,3.0.0,ruby-1.8.7.249,x86_64-linux
-0.00755449999999991,2009-01-08T04:04:55Z,,3.0.0,ruby-1.8.7.249,x86_64-linux
-0.00595999999999997,2009-01-08T04:05:06Z,,3.0.0,ruby-1.8.7.249,x86_64-linux
-0.00740450000000004,2009-01-09T03:54:47Z,,3.0.0,ruby-1.8.7.249,x86_64-linux
-0.00603150000000008,2009-01-09T03:54:57Z,,3.0.0,ruby-1.8.7.249,x86_64-linux
-0.00771250000000012,2009-01-09T15:46:03Z,,3.0.0,ruby-1.8.7.249,x86_64-linux
-```
-
-#### Profiling
-
-In profiling mode, performance tests can generate multiple types of outputs.
-The command line output is always presented but support for the others is
-dependent on the interpreter in use. A brief description of each type and
-their availability across interpreters is given below.
-
-##### Command Line
-
-This is a very basic form of output in profiling mode:
-
-```bash
-BrowsingTest#test_homepage (58 ms warmup)
- process_time: 63 ms
- memory: 832.13 KB
- objects: 7,882
-```
-
-##### Flat
-
-Flat output shows the metric—time, memory, etc—measure in each method.
-[Check Ruby-Prof documentation for a better explanation](http://ruby-prof.rubyforge.org/files/examples/flat_txt.html).
-
-##### Graph
-
-Graph output shows the metric measure in each method, which methods call it and
-which methods it calls. [Check Ruby-Prof documentation for a better explanation](http://ruby-prof.rubyforge.org/files/examples/graph_txt.html).
-
-##### Tree
-
-Tree output is profiling information in calltree format for use by [kcachegrind](http://kcachegrind.sourceforge.net/html/Home.html)
-and similar tools.
-
-##### Output Availability
-
-| | Flat | Graph | Tree |
-| ------------ | ---- | ----- | ---- |
-| **MRI** | yes | yes | yes |
-| **REE** | yes | yes | yes |
-| **Rubinius** | yes | yes | no |
-| **JRuby** | yes | yes | no |
-
-### Tuning Test Runs
-
-Test runs can be tuned by setting the `profile_options` class variable on your
-test class.
-
-```ruby
-require 'test_helper'
-require 'rails/performance_test_help'
-
-class BrowsingTest < ActionDispatch::PerformanceTest
- self.profile_options = { runs: 5, metrics: [:wall_time, :memory] }
-
- test "homepage"
- get '/'
- end
-end
-```
-
-In this example, the test would run 5 times and measure wall time and memory.
-There are a few configurable options:
-
-| Option | Description | Default | Mode |
-| ---------- | ------------------------------------------ | ----------------------------- | --------- |
-| `:runs` | Number of runs. | Benchmarking: 4, Profiling: 1 | Both |
-| `:output` | Directory to use when writing the results. | `tmp/performance` | Both |
-| `:metrics` | Metrics to use. | See below. | Both |
-| `:formats` | Formats to output to. | See below. | Profiling |
-
-Metrics and formats have different defaults depending on the interpreter in use.
-
-| Interpreter | Mode | Default metrics | Default formats |
-| -------------- | ------------ | ------------------------------------------------------- | ----------------------------------------------- |
-| **MRI/REE** | Benchmarking | `[:wall_time, :memory, :objects, :gc_runs, :gc_time]` | N/A |
-| | Profiling | `[:process_time, :memory, :objects]` | `[:flat, :graph_html, :call_tree, :call_stack]` |
-| **Rubinius** | Benchmarking | `[:wall_time, :memory, :objects, :gc_runs, :gc_time]` | N/A |
-| | Profiling | `[:wall_time]` | `[:flat, :graph]` |
-| **JRuby** | Benchmarking | `[:wall_time, :user_time, :memory, :gc_runs, :gc_time]` | N/A |
-| | Profiling | `[:wall_time]` | `[:flat, :graph]` |
-
-As you've probably noticed by now, metrics and formats are specified using a
-symbol array with each name [underscored.](http://api.rubyonrails.org/classes/String.html#method-i-underscore)
-
-### Performance Test Environment
-
-Performance tests are run in the `test` environment. But running performance
-tests will set the following configuration parameters:
-
-```bash
-ActionController::Base.perform_caching = true
-ActiveSupport::Dependencies.mechanism = :require
-Rails.logger.level = ActiveSupport::Logger::INFO
-```
-
-As `ActionController::Base.perform_caching` is set to `true`, performance tests
-will behave much as they do in the `production` environment.
-
-### Installing GC-Patched MRI
-
-To get the best from Rails' performance tests under MRI, you'll need to build
-a special Ruby binary with some super powers.
-
-The recommended patches for each MRI version are:
-
-| Version | Patch |
-| --------------- | --------- |
-| 1.8.6 | ruby186gc |
-| 1.8.7 | ruby187gc |
-| 1.9.2 and above | gcdata |
-
-All of these can be found on [RVM's _patches_ directory](https://github.com/wayneeseguin/rvm/tree/master/patches/ruby)
-under each specific interpreter version.
-
-Concerning the installation itself, you can either do this easily by using
-[RVM](http://rvm.beginrescueend.com) or you can build everything from source,
-which is a little bit harder.
-
-#### Install Using RVM
-
-The process of installing a patched Ruby interpreter is very easy if you let RVM
-do the hard work. All of the following RVM commands will provide you with a
-patched Ruby interpreter:
-
-```bash
-$ rvm install 1.9.2-p180 --patch gcdata
-$ rvm install 1.8.7 --patch ruby187gc
-$ rvm install 1.9.2-p180 --patch ~/Downloads/downloaded_gcdata_patch.patch
-```
-
-You can even keep your regular interpreter by assigning a name to the patched
-one:
-
-```bash
-$ rvm install 1.9.2-p180 --patch gcdata --name gcdata
-$ rvm use 1.9.2-p180 # your regular ruby
-$ rvm use 1.9.2-p180-gcdata # your patched ruby
-```
-
-And it's done! You have installed a patched Ruby interpreter.
-
-#### Install From Source
-
-This process is a bit more complicated, but straightforward nonetheless. If
-you've never compiled a Ruby binary before, follow these steps to build a
-Ruby binary inside your home directory.
-
-##### Download and Extract
-
-```bash
-$ mkdir rubygc
-$ wget <the version you want from ftp://ftp.ruby-lang.org/pub/ruby>
-$ tar -xzvf <ruby-version.tar.gz>
-$ cd <ruby-version>
-```
-
-##### Apply the Patch
-
-```bash
-$ curl http://github.com/wayneeseguin/rvm/raw/master/patches/ruby/1.9.2/p180/gcdata.patch | patch -p0 # if you're on 1.9.2!
-$ curl http://github.com/wayneeseguin/rvm/raw/master/patches/ruby/1.8.7/ruby187gc.patch | patch -p0 # if you're on 1.8.7!
-```
-
-##### Configure and Install
-
-The following will install Ruby in your home directory's `/rubygc` directory.
-Make sure to replace `<homedir>` with a full patch to your actual home
-directory.
-
-```bash
-$ ./configure --prefix=/<homedir>/rubygc
-$ make && make install
-```
-
-##### Prepare Aliases
-
-For convenience, add the following lines in your `~/.profile`:
-
-```bash
-alias gcruby='~/rubygc/bin/ruby'
-alias gcrake='~/rubygc/bin/rake'
-alias gcgem='~/rubygc/bin/gem'
-alias gcirb='~/rubygc/bin/irb'
-alias gcrails='~/rubygc/bin/rails'
-```
-
-Don't forget to use your aliases from now on.
-
-### Using Ruby-Prof on MRI and REE
-
-Add Ruby-Prof to your applications' Gemfile if you want to benchmark/profile
-under MRI or REE:
-
-```ruby
-gem 'ruby-prof'
-```
-
-Now run `bundle install` and you're ready to go.
-
-Command Line Tools
-------------------
-
-Writing performance test cases could be an overkill when you are looking for one
-time tests. Rails ships with two command line tools that enable quick and dirty
-performance testing:
-
-### `benchmarker`
-
-Usage:
-
-```bash
-Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS]
- -r, --runs N Number of runs.
- Default: 4
- -o, --output PATH Directory to use when writing the results.
- Default: tmp/performance
- -m, --metrics a,b,c Metrics to use.
- Default: wall_time,memory,objects,gc_runs,gc_time
-```
-
-Example:
-
-```bash
-$ rails benchmarker 'Item.all' 'CouchItem.all' --runs 3 --metrics wall_time,memory
-```
-
-### `profiler`
-
-Usage:
-
-```bash
-Usage: rails profiler 'Ruby.code' 'Ruby.more_code' ... [OPTS]
- -r, --runs N Number of runs.
- Default: 1
- -o, --output PATH Directory to use when writing the results.
- Default: tmp/performance
- -m, --metrics a,b,c Metrics to use.
- Default: process_time,memory,objects
- -f, --formats x,y,z Formats to output to.
- Default: flat,graph_html,call_tree
-```
-
-Example:
-
-```bash
-$ rails profiler 'Item.all' 'CouchItem.all' --runs 2 --metrics process_time --formats flat
-```
-
-NOTE: Metrics and formats vary from interpreter to interpreter. Pass `--help` to
-each tool to see the defaults for your interpreter.
-
-Helper Methods
---------------
-
-Rails provides various helper methods inside Active Record, Action Controller
-and Action View to measure the time taken by a given piece of code. The method
-is called `benchmark()` in all the three components.
-
-### Model
-
-```ruby
-Project.benchmark("Creating project") do
- project = Project.create("name" => "stuff")
- project.create_manager("name" => "David")
- project.milestones << Milestone.all
-end
-```
-
-This benchmarks the code enclosed in the `Project.benchmark("Creating project") do...end`
-block and prints the result to the log file:
-
-```ruby
-Creating project (185.3ms)
-```
-
-Please refer to the [API docs](http://api.rubyonrails.org/classes/ActiveSupport/Benchmarkable.html#method-i-benchmark)
-for additional options to `benchmark()`.
-
-### Controller
-
-Similarly, you could use this helper method inside [controllers.](http://api.rubyonrails.org/classes/ActiveSupport/Benchmarkable.html)
-
-```ruby
-def process_projects
- benchmark("Processing projects") do
- Project.process(params[:project_ids])
- Project.update_cached_projects
- end
-end
-```
-
-NOTE: `benchmark` is a class method inside controllers.
-
-### View
-
-And in [views](http://api.rubyonrails.org/classes/ActiveSupport/Benchmarkable.html:)
-
-```erb
-<% benchmark("Showing projects partial") do %>
- <%= render @projects %>
-<% end %>
-```
-
-Request Logging
----------------
-
-Rails log files contain very useful information about the time taken to serve
-each request. Here's a typical log file entry:
-
-```bash
-Processing ItemsController#index (for 127.0.0.1 at 2009-01-08 03:06:39) [GET]
-Rendering template within layouts/items
-Rendering items/index
-Completed in 5ms (View: 2, DB: 0) | 200 OK [http://0.0.0.0/items]
-```
-
-For this section, we're only interested in the last line:
-
-```bash
-Completed in 5ms (View: 2, DB: 0) | 200 OK [http://0.0.0.0/items]
-```
-
-This data is fairly straightforward to understand. Rails uses millisecond(ms) as
-the metric to measure the time taken. The complete request spent 5 ms inside
-Rails, out of which 2 ms were spent rendering views and none was spent
-communication with the database. It's safe to assume that the remaining 3 ms
-were spent inside the controller.
-
-Michael Koziarski has an [interesting blog post](http://www.therailsway.com/2009/1/6/requests-per-second)
-explaining the importance of using milliseconds as the metric.
-
-Useful Links
-------------
-
-### Rails Plugins and Gems
-
-* [Rails Analyzer](http://rails-analyzer.rubyforge.org)
-* [Rails Footnotes](https://github.com/josevalim/rails-footnotes/tree/master)
-* [Query Reviewer](https://github.com/nesquena/query_reviewer)
-* [MiniProfiler](http://www.miniprofiler.com)
-
-### Generic Tools
-
-* [httperf](http://www.hpl.hp.com/research/linux/httperf/)
-* [ab](http://httpd.apache.org/docs/2.2/programs/ab.html)
-* [JMeter](http://jakarta.apache.org/jmeter/)
-* [kcachegrind](http://kcachegrind.sourceforge.net/html/Home.html)
-
-### Tutorials and Documentation
-
-* [ruby-prof API Documentation](http://ruby-prof.rubyforge.org)
-* [Request Profiling Railscast](http://railscasts.com/episodes/98-request-profiling) - Outdated, but useful for understanding call graphs.
-
-Commercial Products
--------------------
-
-Rails has been lucky to have a few companies dedicated to Rails-specific
-performance tools. A couple of those are:
-
-* [New Relic](http://www.newrelic.com)
-* [Scout](http://scoutapp.com)
diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md
index 9e694acb98..77138d8871 100644
--- a/guides/source/rails_application_templates.md
+++ b/guides/source/rails_application_templates.md
@@ -34,7 +34,6 @@ Rails templates API is very self explanatory and easy to understand. Here's an e
```ruby
# template.rb
-run "rm public/index.html"
generate(:scaffold, "person name:string")
route "root to: 'people#index'"
rake("db:migrate")
@@ -158,10 +157,10 @@ generate(:scaffold, "person", "name:string", "address:text", "age:number")
### run(command)
-Executes an arbitrary command. Just like the backticks. Let's say you want to remove the `public/index.html` file:
+Executes an arbitrary command. Just like the backticks. Let's say you want to remove the `README.rdoc` file:
```ruby
-run "rm public/index.html"
+run "rm README.rdoc"
```
### rake(command, options = {})
@@ -180,7 +179,7 @@ rake "db:migrate", env: 'production'
### route(routing_code)
-Adds a routing entry to the `config/routes.rb` file. In above steps, we generated a person scaffold and also removed `public/index.html`. Now to make `PeopleController#index` as the default page for the application:
+Adds a routing entry to the `config/routes.rb` file. In above steps, we generated a person scaffold and also removed `README.rdoc`. Now to make `PeopleController#index` as the default page for the application:
```ruby
route "root to: 'person#index'"
diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md
index ac355b4a08..a6119eb433 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -37,11 +37,11 @@ Rails on Rack
Here's how `rails server` creates an instance of `Rack::Server`
```ruby
-Rails::Server.new.tap { |server|
+Rails::Server.new.tap do |server|
require APP_PATH
Dir.chdir(Rails.application.root)
server.start
-}
+end
```
The `Rails::Server` inherits from `Rack::Server` and calls the `Rack::Server#start` method this way:
diff --git a/guides/source/security.md b/guides/source/security.md
index 0b0cfe69c4..3706a61431 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -210,7 +210,7 @@ The HTTP protocol basically provides two main types of requests - GET and POST (
* The interaction _changes the state_ of the resource in a way that the user would perceive (e.g., a subscription to a service), or
* The user is _held accountable for the results_ of the interaction.
-If your web application is RESTful, you might be used to additional HTTP verbs, such as PUT or DELETE. Most of today's web browsers, however do not support them - only GET and POST. Rails uses a hidden `_method` field to handle this barrier.
+If your web application is RESTful, you might be used to additional HTTP verbs, such as PATCH, PUT or DELETE. Most of today's web browsers, however do not support them - only GET and POST. Rails uses a hidden `_method` field to handle this barrier.
_POST requests can be sent automatically, too_. Here is an example for a link which displays www.harmless.com as destination in the browser's status bar. In fact it dynamically creates a new form that sends a POST request.
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 7747318d32..09d6d2d5ee 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -39,10 +39,10 @@ Rails creates a `test` folder for you as soon as you create a Rails project usin
```bash
$ ls -F test
-fixtures/ functional/ integration/ performance/ test_helper.rb unit/
+fixtures/ functional/ integration/ test_helper.rb unit/
```
-The `unit` directory is meant to hold tests for your models, the `functional` directory is meant to hold tests for your controllers, the `integration` directory is meant to hold tests that involve any number of controllers interacting, and the `performance` directory is meant for performance tests.
+The `unit` directory is meant to hold tests for your models, the `functional` directory is meant to hold tests for your controllers and the `integration` directory is meant to hold tests that involve any number of controllers interacting.
Fixtures are a way of organizing test data; they reside in the `fixtures` folder.
@@ -760,14 +760,12 @@ You don't need to set up and run your tests by hand on a test-by-test basis. Rai
| Tasks | Description |
| ------------------------------- | ----------- |
| `rake test` | Runs all unit, functional and integration tests. You can also simply run `rake` as the _test_ target is the default.|
-| `rake test:benchmark` | Benchmark the performance tests|
| `rake test:controllers` | Runs all the controller tests from `test/controllers`|
| `rake test:functionals` | Runs all the functional tests from `test/controllers`, `test/mailers`, and `test/functional`|
| `rake test:helpers` | Runs all the helper tests from `test/helpers`|
| `rake test:integration` | Runs all the integration tests from `test/integration`|
| `rake test:mailers` | Runs all the mailer tests from `test/mailers`|
| `rake test:models` | Runs all the model tests from `test/models`|
-| `rake test:profile` | Profile the performance tests|
| `rake test:recent` | Tests recent changes|
| `rake test:uncommitted` | Runs all the tests which are uncommitted. Supports Subversion and Git|
| `rake test:units` | Runs all the unit tests from `test/models`, `test/helpers`, and `test/unit`|
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index 869490fc90..a7ca531123 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -66,37 +66,38 @@ Here's the simplest way to write JavaScript. You may see it referred to as
'inline JavaScript':
```html
-<a href="#" onclick="alert('Hello, world.')">Here</a>
+<a href="#" onclick="this.style.backgroundColor='#990000'">Paint it red</a>
```
-
-When clicked, the alert will trigger. Here's the problem: what happens when
-we have lots of JavaScript we want to execute on a click?
+When clicked, the link background will become red. Here's the problem: what
+happens when we have lots of JavaScript we want to execute on a click?
```html
-<a href="#" onclick="function fib(n){return n<2?n:fib(n-1)+fib(n-2);};alert('fib of 15 is: ' + fib(15) + '.');">Calculate</a>
+<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';">Paint it green</a>
```
Awkward, right? We could pull the function definition out of the click handler,
and turn it into CoffeeScript:
```coffeescript
-fib = (n) ->
- (if n < 2 then n else fib(n - 1) + fib(n - 2))
+paintIt = (element, backgroundColor, textColor) ->
+ element.style.backgroundColor = backgroundColor
+ if textColor?
+ element.style.color = textColor
```
And then on our page:
```html
-<a href="#" onclick="alert('fib of 15 is: ' + fib(15) + '.');">Calculate</a>
+<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>
```
That's a little bit better, but what about multiple links that have the same
effect?
```html
-<a href="#" onclick="alert('fib of 16 is: ' + fib(16) + '.');">Calculate</a>
-<a href="#" onclick="alert('fib of 17 is: ' + fib(17) + '.');">Calculate</a>
-<a href="#" onclick="alert('fib of 18 is: ' + fib(18) + '.');">Calculate</a>
+<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>
+<a href="#" onclick="paintIt(this, '#009900', '#FFFFFF')">Paint it green</a>
+<a href="#" onclick="paintIt(this, '#000099', '#FFFFFF')">Paint it blue</a>
```
Not very DRY, eh? We can fix this by using events instead. We'll add a `data-*`
@@ -104,19 +105,21 @@ attribute to our link, and then bind a handler to the click event of every link
that has that attribute:
```coffeescript
-fib = (n) ->
- (if n < 2 then n else fib(n - 1) + fib(n - 2))
-
-$(document).ready ->
- $("a[data-fib]").click (e) ->
- count = $(this).data("fib")
- alert "fib of #{count} is: #{fib(count)}."
-
-... later ...
-
-<a href="#" data-fib="15">Calculate</a>
-<a href="#" data-fib="16">Calculate</a>
-<a href="#" data-fib="17">Calculate</a>
+paintIt = (element, backgroundColor, textColor) ->
+ element.style.backgroundColor = backgroundColor
+ if textColor?
+ element.style.color = textColor
+
+$ ->
+ $("a[data-color]").click ->
+ backgroundColor = $(this).data("background-color")
+ textColor = $(this).data("text-color")
+ paintIt(this, backgroundColor, textColor)
+```
+```html
+<a href="#" data-background-color="#990000">Paint it red</a>
+<a href="#" data-background-color="#009900" data-text-color="#FFFFFF">Paint it green</a>
+<a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a>
```
We call this 'unobtrusive' JavaScript because we're no longer mixing our
@@ -214,20 +217,19 @@ which generates
```
You can bind to the same Ajax events as `form_for`. Here's an example. Let's
-assume that we have a resource `/fib/:n` that calculates the `n`th Fibonacci
-number. We would generate some HTML like this:
+assume that we have a list of posts that can be deleted with just one
+click. We would generate some HTML like this:
```erb
-<%= link_to "Calculate", "/fib/15", remote: true, data: { fib: 15 } %>
+<%= link_to "Delete post", @post, remote: true, method: :delete %>
```
and write some CoffeeScript like this:
```coffeescript
-$(document).ready ->
- $("a[data-fib]").on "ajax:success", (e, data, status, xhr) ->
- count = $(this).data("fib")
- alert "fib of #{count} is: #{data}."
+$ ->
+ $("a[data-remote]").on "ajax:success", (e, data, status, xhr) ->
+ alert "The post was deleted."
```
### button_to
diff --git a/install.rb b/install.rb
index bf843e251e..3967624a3f 100644
--- a/install.rb
+++ b/install.rb
@@ -1,5 +1,10 @@
version = ARGV.pop
+if version.nil?
+ puts "Usage: ruby install.rb version"
+ exit(64)
+end
+
%w( activesupport activemodel activerecord actionpack actionmailer railties ).each do |framework|
puts "Installing #{framework}..."
`cd #{framework} && gem build #{framework}.gemspec && gem install #{framework}-#{version}.gem --local --no-ri --no-rdoc && rm #{framework}-#{version}.gem`
diff --git a/rails.gemspec b/rails.gemspec
index ba94354bf1..34957c9455 100644
--- a/rails.gemspec
+++ b/rails.gemspec
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
s.bindir = 'bin'
s.executables = []
- s.files = Dir['guides/**/*']
+ s.files = ['README.rdoc'] + Dir['guides/**/*']
s.add_dependency 'activesupport', version
s.add_dependency 'actionpack', version
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 9bcf53eaea..3b0f282143 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -6,13 +6,87 @@
*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.
+
+ *Brian D. Burns*
+
+* Deprecate `Rails::Generators::ActiveModel#update_attributes` in favor of `#update`.
+
+ ORMs that implement `Generators::ActiveModel#update_attributes` should change
+ to `#update`. Scaffold controller generators should change calls like:
+
+ @orm_instance.update_attributes(...)
+
+ to:
+
+ @orm_instance.update(...)
+
+ This goes along with the addition of `ActiveRecord::Base#update`.
+
+ *Carlos Antonio da Silva*
+
+* Include `jbuilder` by default and rely on its scaffold generator to show json API.
+ Check https://github.com/rails/jbuilder for more info and examples.
+
+ *DHH*
+
+* Scaffold now generates HTML-only controller by default.
+
+ *DHH + Pavel Pravosud*
+
+* The generated `README.rdoc` for new applications invites the user to
+ document the necessary steps to get the application up and running.
+
+ *Xavier Noria*
+
+* Generated applications no longer get `doc/README_FOR_APP`. In consequence,
+ the `doc` directory is created on demand by documentation tasks rather than
+ generated by default.
+
+ *Xavier Noria*
+
+* App executables now live in the `bin/` directory: `bin/bundle`,
+ `bin/rails`, `bin/rake`. Run `rake rails:update:bin` to add these
+ executables to your own app. `script/rails` is gone from new apps.
+
+ Running executables within your app ensures they use your app's Ruby
+ version and its bundled gems, and it ensures your production deployment
+ tools only need to execute a single script. No more having to carefully
+ `cd` to the app dir and run `bundle exec ...`.
+
+ Rather than treating `bin/` as a junk drawer for generated "binstubs",
+ bundler 1.3 adds support for generating stubs for just the executables
+ you actually use: `bundle binstubs unicorn` generates `bin/unicorn`.
+ Add that executable to git and version it just like any other app code.
+
+ *Jeremy Kemper*
+
+* `config.assets.enabled` is now true by default. If you're upgrading from a Rails 3.x app
+ that does not use the asset pipeline, you'll be required to add `config.assets.enabled = false`
+ to your application.rb. If you don't want the asset pipeline on a new app use `--skip-sprockets`
+
+ *DHH*
+
+* Environment name can be a start substring of the default environment names
+ (production, development, test). For example: tes, pro, prod, dev, devel.
+ Fix #8628.
+
+ *Mykola Kyryk*
+
* Add `-B` alias for `--skip-bundle` option in the rails new generators.
*Jiri Pospisil*
* Quote column names in generates fixture files. This prevents
conflicts with reserved YAML keywords such as 'yes' and 'no'
- Fix #8612
+ Fix #8612.
*Yves Senn*
@@ -161,8 +235,6 @@
* Add convenience `hide!` method to Rails generators to hide current generator
namespace from showing when running `rails generate`. *Carlos Antonio da Silva*
-* Scaffold now uses `content_tag_for` in index.html.erb *José Valim*
-
* Rails::Plugin has gone. Instead of adding plugins to vendor/plugins use gems or bundler with path or git dependencies. *Santiago Pastorino*
* Set config.action_mailer.async = true to turn on asynchronous
diff --git a/railties/lib/rails/app_rails_loader.rb b/railties/lib/rails/app_rails_loader.rb
new file mode 100644
index 0000000000..8937e10db3
--- /dev/null
+++ b/railties/lib/rails/app_rails_loader.rb
@@ -0,0 +1,29 @@
+require 'pathname'
+
+module Rails
+ module AppRailsLoader
+ RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"]
+ EXECUTABLE = 'bin/rails'
+
+ def self.exec_app_rails
+ cwd = Dir.pwd
+ return unless in_rails_application_or_engine? || in_rails_application_or_engine_subdirectory?
+ exec RUBY, EXECUTABLE, *ARGV if in_rails_application_or_engine?
+ Dir.chdir("..") do
+ # Recurse in a chdir block: if the search fails we want to be sure
+ # the application is generated in the original working directory.
+ exec_app_rails unless cwd == Dir.pwd
+ end
+ rescue SystemCallError
+ # could not chdir, no problem just return
+ end
+
+ def self.in_rails_application_or_engine?
+ File.exists?(EXECUTABLE) && File.read(EXECUTABLE) =~ /(APP|ENGINE)_PATH/
+ end
+
+ def self.in_rails_application_or_engine_subdirectory?(path = Pathname.new(Dir.pwd))
+ File.exists?(File.join(path, EXECUTABLE)) || !path.root? && in_rails_application_or_engine_subdirectory?(path.parent)
+ end
+ end
+end
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 086fd5ed95..2c7ddd86e7 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -48,12 +48,12 @@ module Rails
@secret_key_base = nil
@assets = ActiveSupport::OrderedOptions.new
- @assets.enabled = false
+ @assets.enabled = true
@assets.paths = []
@assets.precompile = [ Proc.new { |path, fn| fn =~ /app\/assets/ && !%w(.js .css).include?(File.extname(path)) },
/(?:\/|\\|\A)application\.(css|js)$/ ]
@assets.prefix = "/assets"
- @assets.version = ''
+ @assets.version = '1.0'
@assets.debug = false
@assets.compile = true
@assets.digest = false
diff --git a/railties/lib/rails/cli.rb b/railties/lib/rails/cli.rb
index 443d6f47ad..b717b026de 100644
--- a/railties/lib/rails/cli.rb
+++ b/railties/lib/rails/cli.rb
@@ -1,9 +1,12 @@
require 'rbconfig'
-require 'rails/script_rails_loader'
+require 'rails/app_rails_loader'
# If we are inside a Rails application this method performs an exec and thus
# the rest of this script is not run.
-Rails::ScriptRailsLoader.exec_script_rails!
+#
+# TODO: when we hit this, advise adding ./bin to $PATH instead. Then the
+# app's `rails` executable is run immediately.
+Rails::AppRailsLoader.exec_app_rails
require 'rails/ruby_version_check'
Signal.trap("INT") { puts; exit(1) }
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index 3ab2a3809e..aacde52cfc 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -24,8 +24,6 @@ The most common rails commands are:
In addition to those, there are:
application Generate the Rails application code
destroy Undo code generated with "generate" (short-cut alias: "d")
- benchmarker See how fast a piece of code runs
- profiler Get profile information from a piece of code
plugin new Generates skeleton for developing a Rails plugin
runner Run a piece of code in the application environment (short-cut alias: "r")
@@ -51,11 +49,6 @@ when 'generate', 'destroy', 'plugin'
require "rails/commands/#{command}"
end
-when 'benchmarker', 'profiler'
- require APP_PATH
- Rails.application.require_environment!
- require "rails/commands/#{command}"
-
when 'console'
require 'rails/commands/console'
options = Rails::Console.parse_arguments(ARGV)
@@ -72,18 +65,18 @@ when 'console'
when 'server'
# Change to the application's path if there is no config.ru file in current dir.
- # This allows us to run script/rails server from other directories, but still get
+ # This allows us to run `rails server` from other directories, but still get
# the main config.ru and properly set the tmp directory.
Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru"))
require 'rails/commands/server'
- Rails::Server.new.tap { |server|
+ Rails::Server.new.tap do |server|
# We need to require application after the server sets environment,
# otherwise the --environment option given to the server won't propagate.
require APP_PATH
Dir.chdir(Rails.application.root)
server.start
- }
+ end
when 'dbconsole'
require 'rails/commands/dbconsole'
diff --git a/railties/lib/rails/commands/benchmarker.rb b/railties/lib/rails/commands/benchmarker.rb
deleted file mode 100644
index b745b45e17..0000000000
--- a/railties/lib/rails/commands/benchmarker.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require 'optparse'
-require 'rails/test_help'
-require 'rails/performance_test_help'
-
-ARGV.push('--benchmark') # HAX
-require 'active_support/testing/performance'
-ARGV.pop
-
-def options
- options = {}
- defaults = ActiveSupport::Testing::Performance::DEFAULTS
-
- OptionParser.new do |opt|
- opt.banner = "Usage: rails benchmarker 'Ruby.code' 'Ruby.more_code' ... [OPTS]"
- opt.on('-r', '--runs N', Numeric, 'Number of runs.', "Default: #{defaults[:runs]}") { |r| options[:runs] = r }
- opt.on('-o', '--output PATH', String, 'Directory to use when writing the results.', "Default: #{defaults[:output]}") { |o| options[:output] = o }
- opt.on('-m', '--metrics a,b,c', Array, 'Metrics to use.', "Default: #{defaults[:metrics].join(",")}") { |m| options[:metrics] = m.map(&:to_sym) }
- opt.parse!(ARGV)
- end
-
- options
-end
-
-class BenchmarkerTest < ActionDispatch::PerformanceTest #:nodoc:
- self.profile_options = options
-
- ARGV.each do |expression|
- eval <<-RUBY
- def test_#{expression.parameterize('_')}
- #{expression}
- end
- RUBY
- end
-end
diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb
index aef7600fbd..86ab1aabbf 100644
--- a/railties/lib/rails/commands/console.rb
+++ b/railties/lib/rails/commands/console.rb
@@ -24,11 +24,21 @@ module Rails
if arguments.first && arguments.first[0] != '-'
env = arguments.first
- options[:environment] = %w(production development test).detect {|e| e =~ /^#{env}/} || env
+ if available_environments.include? env
+ options[:environment] = env
+ else
+ options[:environment] = %w(production development test).detect {|e| e =~ /^#{env}/} || env
+ end
end
options
end
+
+ private
+
+ def available_environments
+ Dir['config/environments/*.rb'].map { |fname| File.basename(fname, '.*') }
+ end
end
attr_reader :options, :app, :console
@@ -79,13 +89,11 @@ module Rails
end
def require_debugger
- begin
- require 'debugger'
- puts "=> Debugger enabled"
- rescue Exception
- puts "You're missing the 'debugger' gem. Add it to your Gemfile, bundle, and try again."
- exit
- end
+ require 'debugger'
+ puts "=> Debugger enabled"
+ rescue LoadError
+ puts "You're missing the 'debugger' gem. Add it to your Gemfile, bundle, and try again."
+ exit
end
end
end
diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb
index 4a5674236d..5914c9e4ae 100644
--- a/railties/lib/rails/commands/dbconsole.rb
+++ b/railties/lib/rails/commands/dbconsole.rb
@@ -136,12 +136,20 @@ module Rails
if arguments.first && arguments.first[0] != '-'
env = arguments.first
- options[:environment] = %w(production development test).detect {|e| e =~ /^#{env}/} || env
+ if available_environments.include? env
+ options[:environment] = env
+ else
+ options[:environment] = %w(production development test).detect {|e| e =~ /^#{env}/} || env
+ end
end
options
end
+ def available_environments
+ Dir['config/environments/*.rb'].map { |fname| File.basename(fname, '.*') }
+ end
+
def find_cmd_and_exec(commands, *args)
commands = Array(commands)
diff --git a/railties/lib/rails/commands/profiler.rb b/railties/lib/rails/commands/profiler.rb
deleted file mode 100644
index 315bcccf61..0000000000
--- a/railties/lib/rails/commands/profiler.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require 'optparse'
-require 'rails/test_help'
-require 'rails/performance_test_help'
-require 'active_support/testing/performance'
-
-def options
- options = {}
- defaults = ActiveSupport::Testing::Performance::DEFAULTS
-
- OptionParser.new do |opt|
- opt.banner = "Usage: rails profiler 'Ruby.code' 'Ruby.more_code' ... [OPTS]"
- opt.on('-r', '--runs N', Numeric, 'Number of runs.', "Default: #{defaults[:runs]}") { |r| options[:runs] = r }
- opt.on('-o', '--output PATH', String, 'Directory to use when writing the results.', "Default: #{defaults[:output]}") { |o| options[:output] = o }
- opt.on('-m', '--metrics a,b,c', Array, 'Metrics to use.', "Default: #{defaults[:metrics].join(",")}") { |m| options[:metrics] = m.map(&:to_sym) }
- opt.on('-f', '--formats x,y,z', Array, 'Formats to output to.', "Default: #{defaults[:formats].join(",")}") { |m| options[:formats] = m.map(&:to_sym) }
- opt.parse!(ARGV)
- end
-
- options
-end
-
-class ProfilerTest < ActionDispatch::PerformanceTest #:nodoc:
- self.profile_options = options
-
- ARGV.each do |expression|
- eval <<-RUBY
- def test_#{expression.parameterize('_')}
- #{expression}
- end
- RUBY
- end
-end
diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb
index 6adbdc6e0b..c4622d6a2d 100644
--- a/railties/lib/rails/commands/runner.rb
+++ b/railties/lib/rails/commands/runner.rb
@@ -24,7 +24,7 @@ ARGV.clone.options do |opts|
if RbConfig::CONFIG['host_os'] !~ /mswin|mingw/
opts.separator ""
- opts.separator "You can also use runner as a shebang line for your scripts like this:"
+ opts.separator "You can also use runner as a shebang line for your executables:"
opts.separator "-------------------------------------------------------------"
opts.separator "#!/usr/bin/env #{File.expand_path($0)} runner"
opts.separator ""
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 33efcce5ee..46a6485c44 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -175,7 +175,7 @@ module Rails
# There are some places where an Engine's name is used:
#
# * routes: when you mount an Engine with <tt>mount(MyEngine::Engine => '/my_engine')</tt>,
- # it's used as default :as option
+ # it's used as default <tt>:as</tt> option
# * rake task for installing migrations <tt>my_engine:install:migrations</tt>
#
# Engine name is set by default based on class name. For <tt>MyEngine::Engine</tt> it will be
@@ -407,10 +407,8 @@ module Rails
end
end
- self.isolated = false
-
delegate :middleware, :root, :paths, to: :config
- delegate :engine_name, :isolated?, to: "self.class"
+ delegate :engine_name, :isolated?, to: :class
def initialize
@_all_autoload_paths = nil
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index d9a91b74d1..4b767ea0c6 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -50,7 +50,6 @@ module Rails
javascripts: true,
javascript_engine: :js,
orm: false,
- performance_tool: nil,
resource_controller: :controller,
resource_route: true,
scaffold_controller: :scaffold_controller,
@@ -179,7 +178,6 @@ module Rails
"#{test}:model",
"#{test}:scaffold",
"#{test}:view",
- "#{test}:performance",
"#{template}:controller",
"#{template}:scaffold",
"#{template}:mailer",
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index b96ee9295e..cb3aca5811 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -4,6 +4,10 @@ require 'rbconfig'
module Rails
module Generators
module Actions
+ def initialize(*) # :nodoc:
+ super
+ @in_group = nil
+ end
# Adds an entry into Gemfile for the supplied gem.
#
@@ -186,7 +190,7 @@ module Rails
log :generate, what
argument = args.map {|arg| arg.to_s }.flatten.join(" ")
- in_root { run_ruby_script("script/rails generate #{what} #{argument}", verbose: false) }
+ in_root { run_ruby_script("bin/rails generate #{what} #{argument}", verbose: false) }
end
# Runs the supplied rake task
diff --git a/railties/lib/rails/generators/active_model.rb b/railties/lib/rails/generators/active_model.rb
index 6183944bb0..e5373704d7 100644
--- a/railties/lib/rails/generators/active_model.rb
+++ b/railties/lib/rails/generators/active_model.rb
@@ -1,3 +1,5 @@
+require 'active_support/deprecation'
+
module Rails
module Generators
# ActiveModel is a class to be implemented by each ORM to allow Rails to
@@ -63,6 +65,12 @@ module Rails
"#{name}.update(#{params})"
end
+ def update_attributes(*args) # :nodoc:
+ ActiveSupport::Deprecation.warn("Calling '@orm_instance.update_attributes' " \
+ "is deprecated, please use '@orm_instance.update' instead.")
+ update(*args)
+ end
+
# POST create
# PATCH/PUT update
def errors
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
index f5182bcc50..90d8db1df5 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
@@ -3,9 +3,9 @@
<table>
<thead>
<tr>
- <% attributes.each do |attribute| -%>
+<% attributes.each do |attribute| -%>
<th><%= attribute.human_name %></th>
- <% end -%>
+<% end -%>
<th></th>
<th></th>
<th></th>
@@ -13,13 +13,15 @@
</thead>
<tbody>
- <%%= content_tag_for(:tr, @<%= plural_table_name %>) do |<%= singular_table_name %>| %>
- <% attributes.each do |attribute| -%>
+ <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
+ <tr>
+<% attributes.each do |attribute| -%>
<td><%%= <%= singular_table_name %>.<%= attribute.name %> %></td>
- <% end -%>
+<% end -%>
<td><%%= link_to 'Show', <%= singular_table_name %> %></td>
<td><%%= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>) %></td>
<td><%%= link_to 'Destroy', <%= singular_table_name %>, method: :delete, data: { confirm: 'Are you sure?' } %></td>
+ </tr>
<%% end %>
</tbody>
</table>
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 372790df59..e22be40381 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -38,7 +38,7 @@ module Rails
end
def readme
- copy_file "README", "README.rdoc"
+ copy_file "README.rdoc", "README.rdoc"
end
def gemfile
@@ -63,6 +63,13 @@ module Rails
keep_file 'app/models/concerns'
end
+ def bin
+ directory "bin" do |content|
+ "#{shebang}\n" + content
+ end
+ chmod "bin", 0755, verbose: false
+ end
+
def config
empty_directory "config"
@@ -85,10 +92,6 @@ module Rails
directory "db"
end
- def doc
- directory "doc"
- end
-
def lib
empty_directory 'lib'
empty_directory_with_keep_file 'lib/tasks'
@@ -103,13 +106,6 @@ module Rails
directory "public", "public", recursive: false
end
- def script
- directory "script" do |content|
- "#{shebang}\n" + content
- end
- chmod "script", 0755, verbose: false
- end
-
def test
empty_directory_with_keep_file 'test/fixtures'
empty_directory_with_keep_file 'test/controllers'
@@ -118,7 +114,6 @@ module Rails
empty_directory_with_keep_file 'test/helpers'
empty_directory_with_keep_file 'test/integration'
- template 'test/performance/browsing_test.rb'
template 'test/test_helper.rb'
end
@@ -145,7 +140,7 @@ module Rails
# We need to store the RAILS_DEV_PATH in a constant, otherwise the path
# can change in Ruby 1.8.7 when we FileUtils.cd.
RAILS_DEV_PATH = File.expand_path("../../../../../..", File.dirname(__FILE__))
- RESERVED_NAMES = %w[application destroy benchmarker profiler plugin runner test]
+ RESERVED_NAMES = %w[application destroy plugin runner test]
class AppGenerator < AppBase # :nodoc:
add_shared_options_for "application"
@@ -178,6 +173,10 @@ module Rails
build(:app)
end
+ def create_bin_files
+ build(:bin)
+ end
+
def create_config_files
build(:config)
end
@@ -195,10 +194,6 @@ module Rails
build(:db)
end
- def create_doc_files
- build(:doc)
- end
-
def create_lib_files
build(:lib)
end
@@ -211,10 +206,6 @@ module Rails
build(:public_directory)
end
- def create_script_files
- build(:script)
- end
-
def create_test_files
build(:test) unless options[:skip_test_unit]
end
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index c4846b2c11..b5db3d2187 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -9,12 +9,12 @@ source 'https://rubygems.org'
<%= assets_gemfile_entry %>
<%= javascript_gemfile_entry -%>
+# 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'
-# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
-# gem 'jbuilder'
-
# Use unicorn as the app server
# gem 'unicorn'
diff --git a/railties/lib/rails/generators/rails/app/templates/README b/railties/lib/rails/generators/rails/app/templates/README
deleted file mode 100644
index b5d7b6436b..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/README
+++ /dev/null
@@ -1,259 +0,0 @@
-== Welcome to Rails
-
-Rails is a web-application framework that includes everything needed to create
-database-backed web applications according to the Model-View-Control pattern.
-
-This pattern splits the view (also called the presentation) into "dumb"
-templates that are primarily responsible for inserting pre-built data in between
-HTML tags. The model contains the "smart" domain objects (such as Account,
-Product, Person, Post) that holds all the business logic and knows how to
-persist themselves to a database. The controller handles the incoming requests
-(such as Save New Account, Update Product, Show Post) by manipulating the model
-and directing data to the view.
-
-In Rails, the model is handled by what's called an object-relational mapping
-layer entitled Active Record. This layer allows you to present the data from
-database rows as objects and embellish these data objects with business logic
-methods. You can read more about Active Record in
-link:files/vendor/rails/activerecord/README.html.
-
-The controller and view are handled by the Action Pack, which handles both
-layers by its two parts: Action View and Action Controller. These two layers
-are bundled in a single package due to their heavy interdependence. This is
-unlike the relationship between the Active Record and Action Pack that is much
-more separate. Each of these packages can be used independently outside of
-Rails. You can read more about Action Pack in
-link:files/vendor/rails/actionpack/README.html.
-
-
-== Getting Started
-
-1. At the command prompt, create a new Rails application:
- <tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
-
-2. Change directory to <tt>myapp</tt> and start the web server:
- <tt>cd myapp; rails server</tt> (run with --help for options)
-
-3. Go to http://localhost:3000/ and you'll see:
- "Welcome aboard: You're riding Ruby on Rails!"
-
-4. Follow the guidelines to start developing your application. You can find
-the following resources handy:
-
-* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
-* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
-
-
-== Debugging Rails
-
-Sometimes your application goes wrong. Fortunately there are a lot of tools that
-will help you debug it and get it back on the rails.
-
-First area to check is the application log files. Have "tail -f" commands
-running on the server.log and development.log. Rails will automatically display
-debugging and runtime information to these files. Debugging info will also be
-shown in the browser on requests from 127.0.0.1.
-
-You can also log your own messages directly into the log file from your code
-using the Ruby logger class from inside your controllers. Example:
-
- class WeblogController < ActionController::Base
- def destroy
- @weblog = Weblog.find(params[:id])
- @weblog.destroy
- logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
- end
- end
-
-The result will be a message in your log file along the lines of:
-
- Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
-
-More information on how to use the logger is at http://www.ruby-doc.org/core/
-
-Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
-several books available online as well:
-
-* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
-* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
-
-These two books will bring you up to speed on the Ruby language and also on
-programming in general.
-
-
-== Debugger
-
-Debugger support is available through the debugger command when you start your
-Mongrel or WEBrick server with --debugger. This means that you can break out of
-execution at any point in the code, investigate and change the model, and then,
-resume execution! You need to install the 'debugger' gem to run the server in debugging
-mode. Add gem 'debugger' to your Gemfile and run <tt>bundle</tt> to install it. Example:
-
- class WeblogController < ActionController::Base
- def index
- @posts = Post.all
- debugger
- end
- end
-
-So the controller will accept the action, run the first line, then present you
-with a IRB prompt in the server window. Here you can do things like:
-
- >> @posts.inspect
- => "[#<Post:0x14a6be8
- @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>,
- #<Post:0x14a6620
- @attributes={"title"=>"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
- >> @posts.first.title = "hello from a debugger"
- => "hello from a debugger"
-
-...and even better, you can examine how your runtime objects actually work:
-
- >> f = @posts.first
- => #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
- >> f.
- Display all 152 possibilities? (y or n)
-
-Finally, when you're ready to resume execution, you can enter "cont".
-
-
-== Console
-
-The console is a Ruby shell, which allows you to interact with your
-application's domain model. Here you'll have all parts of the application
-configured, just like it is when the application is running. You can inspect
-domain models, change values, and save to the database. Starting the script
-without arguments will launch it in the development environment.
-
-To start the console, run <tt>rails console</tt> from the application
-directory.
-
-Options:
-
-* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications
- made to the database.
-* Passing an environment name as an argument will load the corresponding
- environment. Example: <tt>rails console production</tt>.
-
-To reload your controllers and models after launching the console run
-<tt>reload!</tt>
-
-More information about irb can be found at:
-link:http://www.rubycentral.org/pickaxe/irb.html
-
-
-== dbconsole
-
-You can go to the command line of your database directly through <tt>rails
-dbconsole</tt>. You would be connected to the database with the credentials
-defined in database.yml. Starting the script without arguments will connect you
-to the development database. Passing an argument will connect you to a different
-database, like <tt>rails dbconsole production</tt>. Currently works for MySQL,
-PostgreSQL and SQLite 3.
-
-== Description of Contents
-
-The default directory structure of a generated Ruby on Rails application:
-
- |-- app
- | |-- assets
- | |-- images
- | |-- javascripts
- | `-- stylesheets
- | |-- controllers
- | |-- helpers
- | |-- mailers
- | |-- models
- | `-- views
- | `-- layouts
- |-- config
- | |-- environments
- | |-- initializers
- | `-- locales
- |-- db
- |-- doc
- |-- lib
- | `-- tasks
- |-- log
- |-- public
- |-- script
- |-- test
- | |-- fixtures
- | |-- functional
- | |-- integration
- | |-- performance
- | `-- unit
- |-- tmp
- | |-- cache
- | |-- pids
- | |-- sessions
- | `-- sockets
- `-- vendor
- |-- assets
- `-- stylesheets
-
-app
- Holds all the code that's specific to this particular application.
-
-app/assets
- Contains subdirectories for images, stylesheets, and JavaScript files.
-
-app/controllers
- Holds controllers that should be named like weblogs_controller.rb for
- automated URL mapping. All controllers should descend from
- ApplicationController which itself descends from ActionController::Base.
-
-app/models
- Holds models that should be named like post.rb. Models descend from
- ActiveRecord::Base by default.
-
-app/views
- Holds the template files for the view that should be named like
- weblogs/index.html.erb for the WeblogsController#index action. All views use
- eRuby syntax by default.
-
-app/views/layouts
- Holds the template files for layouts to be used with views. This models the
- common header/footer method of wrapping views. In your views, define a layout
- using the <tt>layout :default</tt> and create a file named default.html.erb.
- Inside default.html.erb, call <% yield %> to render the view using this
- layout.
-
-app/helpers
- Holds view helpers that should be named like weblogs_helper.rb. These are
- generated for you automatically when using generators for controllers.
- Helpers can be used to wrap functionality for your views into methods.
-
-config
- Configuration files for the Rails environment, the routing map, the database,
- and other dependencies.
-
-db
- Contains the database schema in schema.rb. db/migrate contains all the
- sequence of Migrations for your schema.
-
-doc
- This directory is where your application documentation will be stored when
- generated using <tt>rake doc:app</tt>
-
-lib
- Application specific libraries. Basically, any kind of custom code that
- doesn't belong under controllers, models, or helpers. This directory is in
- the load path.
-
-public
- The directory available for the web server. Also contains the dispatchers and the
- default HTML files. This should be set as the DOCUMENT_ROOT of your web
- server.
-
-script
- Helper scripts for automation and generation.
-
-test
- Unit and functional tests along with fixtures. When using the rails generate
- command, template test files will be generated for you and placed in this
- directory.
-
-vendor
- External libraries that the application depends on. If the app has frozen rails,
- those gems also go here, under vendor/rails/. This directory is in the load path.
diff --git a/railties/lib/rails/generators/rails/app/templates/README.rdoc b/railties/lib/rails/generators/rails/app/templates/README.rdoc
new file mode 100644
index 0000000000..dd4e97e22e
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/README.rdoc
@@ -0,0 +1,28 @@
+== README
+
+This README would normally document whatever steps are necessary to get the
+application up and running.
+
+Things you may want to cover:
+
+* Ruby version
+
+* System dependencies
+
+* Configuration
+
+* Database creation
+
+* Database initialization
+
+* How to run the test suite
+
+* Services (job queues, cache servers, search engines, etc.)
+
+* Deployment instructions
+
+* ...
+
+
+Please feel free to use a different markup language if you do not plan to run
+<tt>rake doc:app</tt>.
diff --git a/railties/lib/rails/generators/rails/app/templates/app/mailers/.empty_directory b/railties/lib/rails/generators/rails/app/templates/app/mailers/.empty_directory
deleted file mode 100644
index e69de29bb2..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/app/mailers/.empty_directory
+++ /dev/null
diff --git a/railties/lib/rails/generators/rails/app/templates/app/models/.empty_directory b/railties/lib/rails/generators/rails/app/templates/app/models/.empty_directory
deleted file mode 100644
index e69de29bb2..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/app/models/.empty_directory
+++ /dev/null
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/bundle b/railties/lib/rails/generators/rails/app/templates/bin/bundle
new file mode 100644
index 0000000000..e0df7f4440
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/bin/bundle
@@ -0,0 +1,3 @@
+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/bin/rails b/railties/lib/rails/generators/rails/app/templates/bin/rails
new file mode 100644
index 0000000000..6a128b95e5
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/bin/rails
@@ -0,0 +1,3 @@
+APP_PATH = File.expand_path('../../config/application', __FILE__)
+require_relative '../config/boot'
+require 'rails/commands'
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/rake b/railties/lib/rails/generators/rails/app/templates/bin/rake
new file mode 100644
index 0000000000..d14fc8395b
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/bin/rake
@@ -0,0 +1,3 @@
+require_relative '../config/boot'
+require 'rake'
+Rake.application.run
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 5f15c973c6..f5d7d698a3 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -22,21 +22,10 @@ module <%= app_const_base %>
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
+<% if options.skip_sprockets? -%>
- # 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
-
-<% unless options.skip_sprockets? -%>
- # 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'
+ # Disable the asset pipeline.
+ config.assets.enabled = false
<% end -%>
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
index 22c9194fad..eb569b7dab 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
@@ -26,7 +26,7 @@ development:
# domain socket that doesn't need configuration. Windows does not have
# domain sockets, so uncomment these lines.
#host: localhost
-
+
# The TCP port the server listens on. Defaults to 5432.
# If your server runs on a different port number, change accordingly.
#port: 5432
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
index b52b733c56..53620dc8e2 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlserver.yml
@@ -1,5 +1,5 @@
# SQL Server (2005 or higher recommended)
-#
+#
# Install the adapters and driver
# gem install tiny_tds
# gem install activerecord-sqlserver-adapter
@@ -8,8 +8,8 @@
# gem 'tiny_tds'
# gem 'activerecord-sqlserver-adapter'
#
-# You should make sure freetds is configured correctly first.
-# freetds.conf contains host/port/protocol_versions settings.
+# You should make sure freetds is configured correctly first.
+# freetds.conf contains host/port/protocol_versions settings.
# http://freetds.schemamania.org/userguide/freetdsconf.htm
#
# A typical Microsoft server
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index 68df1d1ec8..0ab91d9864 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -32,6 +32,9 @@
# Generate digests for assets URLs.
config.assets.digest = true
+
+ # Version of your assets, change this if you want to expire all your assets.
+ config.assets.version = '1.0'
<%- end -%>
# Specifies the header that your server uses for sending files.
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb
new file mode 100644
index 0000000000..4a994e1e7b
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/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/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb
index 9262c3379f..ac033bf9dc 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/inflections.rb
@@ -9,7 +9,7 @@
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
-#
+
# These inflection rules are supported but not enabled by default:
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.acronym 'RESTful'
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb
index a8285f88ca..d89dac7c6a 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb
@@ -1,3 +1,5 @@
+# Be sure to restart your server when you modify this file.
+
# 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.
# Rails.application.config.time_zone = 'Central Time (US & Canada)'
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt
index 280f777cc0..4f1d56cd2f 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/wrap_parameters.rb.tt
@@ -1,5 +1,5 @@
# Be sure to restart your server when you modify this file.
-#
+
# This file contains settings for ActionController::ParamsWrapper which
# is enabled by default.
diff --git a/railties/lib/rails/generators/rails/app/templates/doc/README_FOR_APP b/railties/lib/rails/generators/rails/app/templates/doc/README_FOR_APP
deleted file mode 100644
index fe41f5cc24..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/doc/README_FOR_APP
+++ /dev/null
@@ -1,2 +0,0 @@
-Use this README file to introduce your application and point to useful places in the API for learning more.
-Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries.
diff --git a/railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory b/railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory
deleted file mode 100644
index e69de29bb2..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory
+++ /dev/null
diff --git a/railties/lib/rails/generators/rails/app/templates/script/rails b/railties/lib/rails/generators/rails/app/templates/script/rails
deleted file mode 100644
index 11bc1edde9..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/script/rails
+++ /dev/null
@@ -1,5 +0,0 @@
-# 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/railties/lib/rails/generators/rails/app/templates/test/fixtures/.empty_directory b/railties/lib/rails/generators/rails/app/templates/test/fixtures/.empty_directory
deleted file mode 100644
index e69de29bb2..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/test/fixtures/.empty_directory
+++ /dev/null
diff --git a/railties/lib/rails/generators/rails/app/templates/test/functional/.empty_directory b/railties/lib/rails/generators/rails/app/templates/test/functional/.empty_directory
deleted file mode 100644
index e69de29bb2..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/test/functional/.empty_directory
+++ /dev/null
diff --git a/railties/lib/rails/generators/rails/app/templates/test/integration/.empty_directory b/railties/lib/rails/generators/rails/app/templates/test/integration/.empty_directory
deleted file mode 100644
index e69de29bb2..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/test/integration/.empty_directory
+++ /dev/null
diff --git a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb
deleted file mode 100644
index d09ce5ad34..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require 'test_helper'
-require 'rails/performance_test_help'
-
-class BrowsingTest < ActionDispatch::PerformanceTest
- # Refer to the documentation for all available options
- # self.profile_options = { runs: 5, metrics: [:wall_time, :memory],
- # output: 'tmp/performance', formats: [:flat] }
-
- test "homepage" do
- get '/'
- end
-end
diff --git a/railties/lib/rails/generators/rails/app/templates/test/unit/.empty_directory b/railties/lib/rails/generators/rails/app/templates/test/unit/.empty_directory
deleted file mode 100644
index e69de29bb2..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/test/unit/.empty_directory
+++ /dev/null
diff --git a/railties/lib/rails/generators/rails/controller/USAGE b/railties/lib/rails/generators/rails/controller/USAGE
index 9def4af65c..64239ad599 100644
--- a/railties/lib/rails/generators/rails/controller/USAGE
+++ b/railties/lib/rails/generators/rails/controller/USAGE
@@ -6,7 +6,7 @@ Description:
path like 'parent_module/controller_name'.
This generates a controller class in app/controllers and invokes helper,
- template engine and test framework generators.
+ template engine, assets, and test framework generators.
Example:
`rails generate controller CreditCards open debit credit close`
diff --git a/railties/lib/rails/generators/rails/migration/USAGE b/railties/lib/rails/generators/rails/migration/USAGE
index f340ed97f2..baf3d9894f 100644
--- a/railties/lib/rails/generators/rails/migration/USAGE
+++ b/railties/lib/rails/generators/rails/migration/USAGE
@@ -20,3 +20,16 @@ Example:
add_column :posts, :title, :string
add_column :posts, :body, :text
add_column :posts, :published, :boolean
+
+Migration names containing JoinTable will generate join tables for use with
+has_and_belongs_to_many associations.
+
+Example:
+ `rails g migration CreateMediaJoinTable artists musics:uniq`
+
+ will create the migration
+
+ create_join_table :artists, :musics do |t|
+ # t.index [:artist_id, :music_id]
+ t.index [:music_id, :artist_id], unique: true
+ end
diff --git a/railties/lib/rails/generators/rails/model/USAGE b/railties/lib/rails/generators/rails/model/USAGE
index e29e19490e..6574200fbf 100644
--- a/railties/lib/rails/generators/rails/model/USAGE
+++ b/railties/lib/rails/generators/rails/model/USAGE
@@ -21,12 +21,12 @@ Description:
Available field types:
- Just after the field name you can specify a type like text or boolean.
+ Just after the field name you can specify a type like text or boolean.
It will generate the column with the associated SQL type. For instance:
`rails generate model post title:string body:text`
- will generate a title column with a varchar type and a body column with a text
+ will generate a title column with a varchar type and a body column with a text
type. You can use the following types:
integer
@@ -57,16 +57,16 @@ Available field types:
limit Set the maximum size of the field giving a number between curly braces
default Set a default value for the field
- precision Defines the precision for the decimal fields
+ precision Defines the precision for the decimal fields
scale Defines the scale for the decimal fields
- uniq Defines the field values as unique
+ uniq Defines the field values as unique
index Will add an index on the field
Examples:
`rails generate model user pseudo:string{30}`
`rails generate model user pseudo:string:uniq`
-
+
Examples:
`rails generate model account`
@@ -76,7 +76,7 @@ Examples:
Model: app/models/account.rb
Test: test/models/account_test.rb
Fixtures: test/fixtures/accounts.yml
- Migration: db/migrate/XXX_add_accounts.rb
+ Migration: db/migrate/XXX_create_accounts.rb
`rails generate model post title:string body:text published:boolean`
@@ -90,5 +90,5 @@ Examples:
Model: app/models/admin/account.rb
Test: test/models/admin/account_test.rb
Fixtures: test/fixtures/admin/accounts.yml
- Migration: db/migrate/XXX_add_admin_accounts.rb
+ Migration: db/migrate/XXX_create_admin_accounts.rb
diff --git a/railties/lib/rails/generators/rails/performance_test/USAGE b/railties/lib/rails/generators/rails/performance_test/USAGE
deleted file mode 100644
index 9dc799559c..0000000000
--- a/railties/lib/rails/generators/rails/performance_test/USAGE
+++ /dev/null
@@ -1,10 +0,0 @@
-Description:
- Stubs out a new performance test. Pass the name of the test, either
- CamelCased or under_scored, as an argument.
-
- This generator invokes the current performance tool, which defaults to
- TestUnit.
-
-Example:
- `rails generate performance_test GeneralStories` creates a GeneralStories
- performance test in test/performance/general_stories_test.rb
diff --git a/railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb b/railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb
deleted file mode 100644
index 56cd562f3d..0000000000
--- a/railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Rails
- module Generators
- class PerformanceTestGenerator < NamedBase # :nodoc:
- hook_for :performance_tool, as: :performance
- end
- end
-end
diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
index 48ce3e86a1..af00748037 100644
--- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
@@ -53,13 +53,11 @@ module Rails
template "lib/%name%.rb"
template "lib/tasks/%name%_tasks.rake"
template "lib/%name%/version.rb"
- if full?
- template "lib/%name%/engine.rb"
- end
+ template "lib/%name%/engine.rb" if engine?
end
def config
- template "config/routes.rb" if full?
+ template "config/routes.rb" if engine?
end
def test
@@ -70,7 +68,7 @@ module Rails
task default: :test
EOF
- if full?
+ if engine?
template "test/integration/navigation_test.rb"
end
end
@@ -132,13 +130,13 @@ task default: :test
end
end
- def script(force = false)
- return unless full?
+ def bin(force = false)
+ return unless engine?
- directory "script", force: force do |content|
+ directory "bin", force: force do |content|
"#{shebang}\n" + content
end
- chmod "script", 0755, verbose: false
+ chmod "bin", 0755, verbose: false
end
def gemfile_entry
@@ -175,7 +173,7 @@ task default: :test
"skip adding entry to Gemfile"
def initialize(*args)
- raise Error, "Options should be given after the plugin name. For details run: rails plugin --help" if args[0].blank?
+ raise Error, "Options should be given after the plugin name. For details run: rails plugin new --help" if args[0].blank?
@dummy_path = nil
super
@@ -216,8 +214,8 @@ task default: :test
build(:images)
end
- def create_script_files
- build(:script)
+ def create_bin_files
+ build(:bin)
end
def create_test_files
@@ -266,13 +264,17 @@ task default: :test
store_application_definition!
build(:test_dummy_config)
build(:test_dummy_clean)
- # ensure that script/rails has proper dummy_path
- build(:script, true)
+ # ensure that bin/rails has proper dummy_path
+ build(:bin, true)
end
end
+ def engine?
+ full? || mountable?
+ end
+
def full?
- options[:full] || options[:mountable]
+ options[:full]
end
def mountable?
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec b/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec
index 568ed653b7..e956d13d8a 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
<% end -%>
<%= '# ' if options.dev? || options.edge? -%>s.add_dependency "rails", "~> <%= Rails::VERSION::STRING %>"
-<% if full? && !options[:skip_javascript] -%>
+<% if engine? && !options[:skip_javascript] -%>
# s.add_dependency "<%= "#{options[:javascript]}-rails" %>"
<% end -%>
<% unless options[:skip_active_record] -%>
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile b/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile
index d69f943a72..a8b5bfaf3f 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile
@@ -2,7 +2,7 @@ source "https://rubygems.org"
<% if options[:skip_gemspec] -%>
<%= '# ' if options.dev? || options.edge? -%>gem "rails", "~> <%= Rails::VERSION::STRING %>"
-<% if full? && !options[:skip_javascript] -%>
+<% if engine? && !options[:skip_javascript] -%>
# gem "<%= "#{options[:javascript]}-rails" %>"
<% end -%>
<% else -%>
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile
index 65a5bae712..0ba899176c 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile
@@ -14,7 +14,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_files.include('lib/**/*.rb')
end
-<% if full? && !options[:skip_active_record] && with_dummy_app? -%>
+<% if engine? && !options[:skip_active_record] && with_dummy_app? -%>
APP_RAKEFILE = File.expand_path("../<%= dummy_path -%>/Rakefile", __FILE__)
load 'rails/tasks/engine.rake'
<% end %>
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/script/rails.tt b/railties/lib/rails/generators/rails/plugin_new/templates/bin/rails.tt
index aa87d1b50c..aa87d1b50c 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/script/rails.tt
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/bin/rails.tt
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%.rb b/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%.rb
index 2d3bdc510c..40c074cced 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/lib/%name%.rb
@@ -1,4 +1,4 @@
-<% if full? -%>
+<% if engine? -%>
require "<%= name %>/engine"
<% end -%>
diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
index dd636ed3cf..36589d65e2 100644
--- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
@@ -9,13 +9,8 @@ module Rails
class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
class_option :stylesheet_engine, desc: "Engine for Stylesheets"
- class_option :html, type: :boolean, default: true,
- desc: "Generate a scaffold with HTML output"
-
def handle_skip
- if !options[:html] || !options[:stylesheets]
- @options = @options.merge(stylesheet_engine: false)
- end
+ @options = @options.merge(stylesheet_engine: false) unless options[:stylesheets]
end
hook_for :scaffold_controller, required: true
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
index 32fa54a362..4f36b612ae 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
@@ -10,17 +10,8 @@ module Rails
class_option :orm, banner: "NAME", type: :string, required: true,
desc: "ORM to generate the controller for"
- class_option :html, type: :boolean, default: true,
- desc: "Generate a scaffold with HTML output"
-
argument :attributes, type: :array, default: [], banner: "field:type field:type"
- def handle_skip
- unless options[:html]
- @options = @options.merge(template_engine: false, helper: false)
- end
- end
-
def create_controller_files
template "controller.rb", File.join('app/controllers', class_path, "#{controller_file_name}_controller.rb")
end
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 2512ce0af1..e813437d75 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
@@ -7,95 +7,47 @@ class <%= controller_class_name %>Controller < ApplicationController
before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy]
# GET <%= route_url %>
- # GET <%= route_url %>.json
def index
@<%= plural_table_name %> = <%= orm_class.all(class_name) %>
-
- respond_to do |format|
- <%- if options[:html] -%>
- format.html # index.html.erb
- <%- end -%>
- format.json { render json: <%= "@#{plural_table_name}" %> }
- end
end
# GET <%= route_url %>/1
- # GET <%= route_url %>/1.json
def show
- respond_to do |format|
- <%- if options[:html] -%>
- format.html # show.html.erb
- <%- end -%>
- format.json { render json: <%= "@#{singular_table_name}" %> }
- end
end
- <%- if options[:html] -%>
# GET <%= route_url %>/new
- # GET <%= route_url %>/new.json
def new
@<%= singular_table_name %> = <%= orm_class.build(class_name) %>
-
- respond_to do |format|
- format.html # new.html.erb
- format.json { render json: <%= "@#{singular_table_name}" %> }
- end
end
# GET <%= route_url %>/1/edit
def edit
end
- <%- end -%>
# POST <%= route_url %>
- # POST <%= route_url %>.json
def create
@<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
- respond_to do |format|
- if @<%= orm_instance.save %>
- <%- if options[:html] -%>
- format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %> }
- <%- end -%>
- format.json { render json: <%= "@#{singular_table_name}" %>, status: :created, location: <%= "@#{singular_table_name}" %> }
- else
- <%- if options[:html] -%>
- format.html { render action: "new" }
- <%- end -%>
- format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity }
- end
+ if @<%= orm_instance.save %>
+ redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %>
+ else
+ render action: 'new'
end
end
# PATCH/PUT <%= route_url %>/1
- # PATCH/PUT <%= route_url %>/1.json
def update
- respond_to do |format|
- if @<%= orm_instance.update("#{singular_table_name}_params") %>
- <%- if options[:html] -%>
- format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> }
- <%- end -%>
- format.json { head :no_content }
- else
- <%- if options[:html] -%>
- format.html { render action: "edit" }
- <%- end -%>
- format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity }
- end
+ if @<%= orm_instance.update("#{singular_table_name}_params") %>
+ redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %>
+ else
+ render action: 'edit'
end
end
# DELETE <%= route_url %>/1
- # DELETE <%= route_url %>/1.json
def destroy
@<%= orm_instance.destroy %>
-
- respond_to do |format|
- <%- if options[:html] -%>
- format.html { redirect_to <%= index_helper %>_url }
- <%- end -%>
- format.json { head :no_content }
- end
+ redirect_to <%= index_helper %>_url
end
private
@@ -104,11 +56,7 @@ class <%= controller_class_name %>Controller < ApplicationController
@<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
end
- # Use this method to whitelist the permissible parameters. Example:
- # params.require(:person).permit(:name, :age)
- #
- # Also, you can specialize this method with per-user checking of permissible
- # attributes.
+ # Never trust parameters from the scary internet, only allow the white list through.
def <%= "#{singular_table_name}_params" %>
<%- if attributes_names.empty? -%>
params[<%= ":#{singular_table_name}" %>]
diff --git a/railties/lib/rails/generators/test_unit/performance/performance_generator.rb b/railties/lib/rails/generators/test_unit/performance/performance_generator.rb
deleted file mode 100644
index 5552edeee4..0000000000
--- a/railties/lib/rails/generators/test_unit/performance/performance_generator.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'rails/generators/test_unit'
-
-module TestUnit # :nodoc:
- module Generators # :nodoc:
- class PerformanceGenerator < Base # :nodoc:
- check_class_collision suffix: "Test"
-
- def create_test_files
- template 'performance_test.rb', File.join('test/performance', class_path, "#{file_name}_test.rb")
- end
- end
- end
-end
diff --git a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb
deleted file mode 100644
index 2bcb482d68..0000000000
--- a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require 'test_helper'
-require 'rails/performance_test_help'
-
-class <%= class_name %>Test < ActionDispatch::PerformanceTest
- # Refer to the documentation for all available options
- # self.profile_options = { runs: 5, metrics: [:wall_time, :memory],
- # output: 'tmp/performance', formats: [:flat] }
-
- test "homepage" do
- get '/'
- end
-end
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/info_controller.rb b/railties/lib/rails/info_controller.rb
index e650f58d20..fa5668a5b5 100644
--- a/railties/lib/rails/info_controller.rb
+++ b/railties/lib/rails/info_controller.rb
@@ -3,12 +3,12 @@ require 'action_dispatch/routing/inspector'
class Rails::InfoController < ActionController::Base # :nodoc:
self.view_paths = File.expand_path('../templates', __FILE__)
prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH
- layout 'application'
+ layout -> { request.xhr? ? nil : 'application' }
before_filter :require_local!
def index
- redirect_to '/rails/info/routes'
+ redirect_to action: :routes
end
def properties
@@ -16,7 +16,7 @@ class Rails::InfoController < ActionController::Base # :nodoc:
end
def routes
- @routes = ActionDispatch::Routing::RoutesInspector.new.collect_routes(_routes.routes)
+ @routes_inspector = ActionDispatch::Routing::RoutesInspector.new(_routes.routes)
end
protected
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index e52d1a8b90..de6795eda2 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -55,8 +55,8 @@ module Rails
add(path, with: value, glob: glob)
end
- def add(path, options={})
- with = Array(options[:with] || path)
+ def add(path, options = {})
+ with = Array(options.fetch(:with, path))
@root[path] = Path.new(self, path, with, options)
end
@@ -189,9 +189,9 @@ module Rails
path = File.expand_path(p, @root.path)
if @glob && File.directory?(path)
- result.concat Dir.chdir(path) {
- Dir.glob(@glob).map { |file| File.join path, file }.sort
- }
+ Dir.chdir(path) do
+ result.concat(Dir.glob(@glob).map { |file| File.join path, file }.sort)
+ end
else
result << path
end
diff --git a/railties/lib/rails/performance_test_help.rb b/railties/lib/rails/performance_test_help.rb
deleted file mode 100644
index b1285efde2..0000000000
--- a/railties/lib/rails/performance_test_help.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-ActionController::Base.perform_caching = true
-ActiveSupport::Dependencies.mechanism = :require
-Rails.logger.level = ActiveSupport::Logger::INFO
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
index 7be2333981..6ed6722c44 100644
--- a/railties/lib/rails/rack/logger.rb
+++ b/railties/lib/rails/rack/logger.rb
@@ -1,19 +1,24 @@
require 'active_support/core_ext/time/conversions'
require 'active_support/core_ext/object/blank'
+require 'active_support/log_subscriber'
+require 'action_dispatch/http/request'
+require 'rack/body_proxy'
module Rails
module Rack
# Sets log tags, logs the request, calls the app, and flushes the logs.
class Logger < ActiveSupport::LogSubscriber
def initialize(app, taggers = nil)
- @app, @taggers = app, taggers || []
+ @app = app
+ @taggers = taggers || []
+ @instrumenter = ActiveSupport::Notifications.instrumenter
end
def call(env)
request = ActionDispatch::Request.new(env)
- if Rails.logger.respond_to?(:tagged)
- Rails.logger.tagged(compute_tags(request)) { call_app(request, env) }
+ if logger.respond_to?(:tagged)
+ logger.tagged(compute_tags(request)) { call_app(request, env) }
else
call_app(request, env)
end
@@ -23,13 +28,19 @@ module Rails
def call_app(request, env)
# Put some space between requests in development logs.
- if Rails.env.development?
- Rails.logger.debug ''
- Rails.logger.debug ''
+ if development?
+ logger.debug ''
+ logger.debug ''
end
- Rails.logger.info started_request_message(request)
- @app.call(env)
+ @instrumenter.start 'action_dispatch.request', request: request
+ logger.info started_request_message(request)
+ resp = @app.call(env)
+ resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) }
+ resp
+ rescue
+ finish(request)
+ raise
ensure
ActiveSupport::LogSubscriber.flush_all!
end
@@ -55,6 +66,20 @@ module Rails
end
end
end
+
+ private
+
+ def finish(request)
+ @instrumenter.finish 'action_dispatch.request', request: request
+ end
+
+ def development?
+ Rails.env.development?
+ end
+
+ def logger
+ Rails.logger
+ end
end
end
end
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index 5b454e7f20..9437e9c406 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -172,7 +172,7 @@ module Rails
end
end
- delegate :railtie_name, to: "self.class"
+ delegate :railtie_name, to: :class
def config
@config ||= Railtie::Configuration.new
diff --git a/railties/lib/rails/script_rails_loader.rb b/railties/lib/rails/script_rails_loader.rb
deleted file mode 100644
index 7054089614..0000000000
--- a/railties/lib/rails/script_rails_loader.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'pathname'
-
-module Rails
- module ScriptRailsLoader
- RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"]
- SCRIPT_RAILS = File.join('script', 'rails')
-
- def self.exec_script_rails!
- cwd = Dir.pwd
- return unless in_rails_application? || in_rails_application_subdirectory?
- exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application?
- Dir.chdir("..") do
- # Recurse in a chdir block: if the search fails we want to be sure
- # the application is generated in the original working directory.
- exec_script_rails! unless cwd == Dir.pwd
- end
- rescue SystemCallError
- # could not chdir, no problem just return
- end
-
- def self.in_rails_application?
- File.exists?(SCRIPT_RAILS)
- end
-
- def self.in_rails_application_subdirectory?(path = Pathname.new(Dir.pwd))
- File.exists?(File.join(path, SCRIPT_RAILS)) || !path.root? && in_rails_application_subdirectory?(path.parent)
- end
- end
-end \ No newline at end of file
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index ac806e8006..2cbb0a435c 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -15,7 +15,7 @@
class SourceAnnotationExtractor
class Annotation < Struct.new(:line, :tag, :text)
def self.directories
- @@directories ||= %w(app config db lib script test) + (ENV['SOURCE_ANNOTATION_DIRECTORIES'] || '').split(',')
+ @@directories ||= %w(app config db lib test) + (ENV['SOURCE_ANNOTATION_DIRECTORIES'] || '').split(',')
end
# Returns a representation of the annotation that looks like this:
@@ -31,16 +31,25 @@ class SourceAnnotationExtractor
end
end
- # Prints all annotations with tag +tag+ under the root directories +app+, +config+, +lib+,
- # +script+, and +test+ (recursively). Filenames with extension
- # +.builder+, +.rb+, +.erb+, +.haml+, +.slim+, +.css+, +.scss+, +.js+,
- # +.coffee+, and +.rake+ are taken into account. The +options+ hash is passed to each
- # annotation's +to_s+.
+ # Prints all annotations with tag +tag+ under the root directories +app+,
+ # +config+, +db+, +lib+, and +test+ (recursively).
+ #
+ # Additional directories may be added using a comma-delimited list set using
+ # <tt>ENV['SOURCE_ANNOTATION_DIRECTORIES']</tt>.
+ #
+ # Directories may also be explicitly set using the <tt>:dirs</tt> key in +options+.
+ #
+ # SourceAnnotationExtractor.enumerate 'TODO|FIXME', dirs: %w(app lib), tag: true
+ #
+ # If +options+ has a <tt>:tag</tt> flag, it will be passed to each annotation's +to_s+.
+ #
+ # See <tt>#find_in</tt> for a list of file extensions that will be taken into account.
#
# This class method is the single entry point for the rake tasks.
def self.enumerate(tag, options={})
extractor = new(tag)
- extractor.display(extractor.find, options)
+ dirs = options.delete(:dirs) || Annotation.directories
+ extractor.display(extractor.find(dirs), options)
end
attr_reader :tag
@@ -51,7 +60,7 @@ class SourceAnnotationExtractor
# Returns a hash that maps filenames under +dirs+ (recursively) to arrays
# with their annotations.
- def find(dirs = Annotation.directories)
+ def find(dirs)
dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
end
@@ -68,16 +77,22 @@ class SourceAnnotationExtractor
if File.directory?(item)
results.update(find_in(item))
- elsif item =~ /\.(builder|rb|coffee|rake)$/
- results.update(extract_annotations_from(item, /#\s*(#{tag}):?\s*(.*)$/))
- elsif item =~ /\.(css|scss|js)$/
- results.update(extract_annotations_from(item, /\/\/\s*(#{tag}):?\s*(.*)$/))
- elsif item =~ /\.erb$/
- results.update(extract_annotations_from(item, /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/))
- elsif item =~ /\.haml$/
- results.update(extract_annotations_from(item, /-\s*#\s*(#{tag}):?\s*(.*)$/))
- elsif item =~ /\.slim$/
- results.update(extract_annotations_from(item, /\/\s*\s*(#{tag}):?\s*(.*)$/))
+ else
+ pattern =
+ case item
+ when /\.(builder|rb|coffee|rake)$/
+ /#\s*(#{tag}):?\s*(.*)$/
+ when /\.(css|scss|js)$/
+ /\/\/\s*(#{tag}):?\s*(.*)$/
+ when /\.erb$/
+ /<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/
+ when /\.haml$/
+ /-\s*#\s*(#{tag}):?\s*(.*)$/
+ when /\.slim$/
+ /\/\s*\s*(#{tag}):?\s*(.*)$/
+ else nil
+ end
+ results.update(extract_annotations_from(item, pattern)) if pattern
end
end
diff --git a/railties/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake
index 2851ca4189..ea6c074bdc 100644
--- a/railties/lib/rails/tasks/documentation.rake
+++ b/railties/lib/rails/tasks/documentation.rake
@@ -45,7 +45,7 @@ namespace :doc do
rdoc.title = ENV['title'] || "Rails Application Documentation"
rdoc.options << '--line-numbers'
rdoc.options << '--charset' << 'utf-8'
- rdoc.rdoc_files.include('doc/README_FOR_APP')
+ rdoc.rdoc_files.include('README.rdoc')
rdoc.rdoc_files.include('app/**/*.rb')
rdoc.rdoc_files.include('lib/**/*.rb')
}
@@ -57,7 +57,10 @@ namespace :doc do
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
rdoc.title = "Rails Framework Documentation"
rdoc.options << '--line-numbers'
- rdoc.rdoc_files.include('README.rdoc')
+
+ gem_path('rails') do |rails|
+ rdoc.options << '-m' << "#{rails}/README.rdoc"
+ end
gem_path('actionmailer') do |actionmailer|
%w(README.rdoc CHANGELOG.md MIT-LICENSE lib/action_mailer/base.rb).each do |file|
diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake
index 50499304cb..2116330b45 100644
--- a/railties/lib/rails/tasks/framework.rake
+++ b/railties/lib/rails/tasks/framework.rake
@@ -1,6 +1,6 @@
namespace :rails do
- desc "Update configs and some other initially generated files (or use just update:configs, update:scripts, or update:application_controller)"
- task update: [ "update:configs", "update:scripts", "update:application_controller" ]
+ desc "Update configs and some other initially generated files (or use just update:configs, update:bin, or update:application_controller)"
+ task update: [ "update:configs", "update:bin", "update:application_controller" ]
desc "Applies the template supplied by LOCATION=(/path/to/template) or URL"
task :template do
@@ -58,9 +58,9 @@ namespace :rails do
invoke_from_app_generator :create_config_files
end
- # desc "Adds new scripts to the application script/ directory"
- task :scripts do
- invoke_from_app_generator :create_script_files
+ # desc "Adds new executables to the application bin/ directory"
+ task :bin do
+ invoke_from_app_generator :create_bin_files
end
# desc "Rename application.rb to application_controller.rb"
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/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake
index 676b475640..1815c2fdc7 100644
--- a/railties/lib/rails/tasks/routes.rake
+++ b/railties/lib/rails/tasks/routes.rake
@@ -2,6 +2,6 @@ desc 'Print out all defined routes in match order, with names. Target specific c
task routes: :environment do
all_routes = Rails.application.routes.routes
require 'action_dispatch/routing/inspector'
- inspector = ActionDispatch::Routing::RoutesInspector.new
- puts inspector.format(all_routes, ENV['CONTROLLER']).join "\n"
+ inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
+ puts inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, ENV['CONTROLLER'])
end
diff --git a/railties/lib/rails/templates/layouts/application.html.erb b/railties/lib/rails/templates/layouts/application.html.erb
index 3b09ef53a3..7352d48e7b 100644
--- a/railties/lib/rails/templates/layouts/application.html.erb
+++ b/railties/lib/rails/templates/layouts/application.html.erb
@@ -24,6 +24,8 @@
a:hover { color: #fff; background-color:#000; }
h2 { padding-left: 10px; }
+
+ <%= yield :style %>
</style>
</head>
<body>
diff --git a/railties/lib/rails/templates/rails/info/routes.html.erb b/railties/lib/rails/templates/rails/info/routes.html.erb
index 1ea387c63f..2d8a190986 100644
--- a/railties/lib/rails/templates/rails/info/routes.html.erb
+++ b/railties/lib/rails/templates/rails/info/routes.html.erb
@@ -6,7 +6,4 @@
Routes match in priority from top to bottom
</p>
-<%# actionpack/lib/action_dispatch/middleware/templates %>
-<%= render layout: "routes/route_wrapper" do %>
- <%= render partial: "routes/route", collection: @routes %>
-<% end %>
+<%= @routes_inspector.format(ActionDispatch::Routing::HtmlTableFormatter.new(self)) %>
diff --git a/railties/lib/rails/templates/rails/welcome/index.html.erb b/railties/lib/rails/templates/rails/welcome/index.html.erb
index 9a62d206dc..e239e1695e 100644
--- a/railties/lib/rails/templates/rails/welcome/index.html.erb
+++ b/railties/lib/rails/templates/rails/welcome/index.html.erb
@@ -59,7 +59,7 @@
#header {
- background-image: url("assets/rails.png");
+ background-image: url("/assets/rails.png");
background-repeat: no-repeat;
background-position: top left;
height: 64px;
@@ -173,15 +173,18 @@
</style>
<script>
function about() {
- info = document.getElementById('about-content');
- if (window.XMLHttpRequest)
- { xhr = new XMLHttpRequest(); }
- else
- { xhr = new ActiveXObject("Microsoft.XMLHTTP"); }
- xhr.open("GET","rails/info/properties",false);
- xhr.send("");
- info.innerHTML = xhr.responseText;
- info.style.display = 'block'
+ var info = document.getElementById('about-content'),
+ xhr;
+
+ if (info.innerHTML === '') {
+ xhr = new XMLHttpRequest();
+ xhr.open("GET", "/rails/info/properties", false);
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+ xhr.send("");
+ info.innerHTML = xhr.responseText;
+ }
+
+ info.style.display = info.style.display === 'none' ? 'block' : 'none';
}
</script>
</head>
@@ -208,7 +211,7 @@
</div>
<div id="about">
- <h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
+ <h3><a href="/rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
<div id="about-content" style="display: none"></div>
</div>
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
index 616206dd0b..739e4ad992 100644
--- a/railties/lib/rails/test_help.rb
+++ b/railties/lib/rails/test_help.rb
@@ -11,16 +11,6 @@ require 'action_dispatch/testing/integration'
require 'rails/backtrace_cleaner'
MiniTest.backtrace_filter = Rails.backtrace_cleaner
-# Enable turn if it is available
-begin
- require 'turn'
-
- Turn.config do |c|
- c.natural = true
- end
-rescue LoadError
-end
-
if defined?(ActiveRecord::Base)
class ActiveSupport::TestCase
include ActiveRecord::TestFixtures
diff --git a/railties/lib/rails/test_unit/railtie.rb b/railties/lib/rails/test_unit/railtie.rb
index ed89ce4128..f52c4c44b7 100644
--- a/railties/lib/rails/test_unit/railtie.rb
+++ b/railties/lib/rails/test_unit/railtie.rb
@@ -5,7 +5,6 @@ module Rails
fixture_replacement: nil
c.integration_tool :test_unit
- c.performance_tool :test_unit
end
rake_tasks do
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 9ad3a4e6d6..f0d46fd959 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -45,7 +45,7 @@ end
task default: :test
-desc 'Runs test:units, test:functionals, test:integration together (also available: test:benchmark, test:profile)'
+desc 'Runs test:units, test:functionals, test:integration together'
task :test do
Rake::Task[ENV['TEST'] ? 'test:single' : 'test:run'].invoke
end
@@ -146,15 +146,4 @@ namespace :test do
t.libs << "test"
t.pattern = 'test/integration/**/*_test.rb'
end
-
- Rails::SubTestTask.new(benchmark: 'test:prepare') do |t|
- t.libs << 'test'
- t.pattern = 'test/performance/**/*_test.rb'
- t.options = '-- --benchmark'
- end
-
- Rails::SubTestTask.new(profile: 'test:prepare') do |t|
- t.libs << 'test'
- t.pattern = 'test/performance/**/*_test.rb'
- end
end
diff --git a/railties/test/app_rails_loader_test.rb b/railties/test/app_rails_loader_test.rb
new file mode 100644
index 0000000000..87e0ad7bd7
--- /dev/null
+++ b/railties/test/app_rails_loader_test.rb
@@ -0,0 +1,41 @@
+require 'abstract_unit'
+require 'rails/app_rails_loader'
+
+class AppRailsLoaderTest < ActiveSupport::TestCase
+ test "is in a rails application if bin/rails exists and contains APP_PATH" do
+ File.stubs(:exists?).returns(true)
+ File.stubs(:read).with('bin/rails').returns('APP_PATH')
+ assert Rails::AppRailsLoader.in_rails_application_or_engine?
+ end
+
+ test "is not in a rails application if bin/rails exists but doesn't contain APP_PATH" do
+ File.stubs(:exists?).returns(true)
+ File.stubs(:read).with('bin/rails').returns('railties bin/rails')
+ assert !Rails::AppRailsLoader.in_rails_application_or_engine?
+ end
+
+ test "is in a rails application if parent directory has bin/rails containing APP_PATH" do
+ File.stubs(:exists?).with("/foo/bar/bin/rails").returns(false)
+ File.stubs(:exists?).with("/foo/bin/rails").returns(true)
+ File.stubs(:read).with('/foo/bin/rails').returns('APP_PATH')
+ assert Rails::AppRailsLoader.in_rails_application_or_engine_subdirectory?(Pathname.new("/foo/bar"))
+ end
+
+ test "is not in a rails application if at the root directory and doesn't have bin/rails" do
+ Pathname.any_instance.stubs(:root?).returns true
+ assert !Rails::AppRailsLoader.in_rails_application_or_engine?
+ end
+
+ test "is in a rails engine if parent directory has bin/rails containing ENGINE_PATH" do
+ File.stubs(:exists?).with("/foo/bar/bin/rails").returns(false)
+ File.stubs(:exists?).with("/foo/bin/rails").returns(true)
+ File.stubs(:read).with('/foo/bin/rails').returns('ENGINE_PATH')
+ assert Rails::AppRailsLoader.in_rails_application_or_engine_subdirectory?(Pathname.new("/foo/bar"))
+ end
+
+ test "is in a rails engine if bin/rails exists containing ENGINE_PATH" do
+ File.stubs(:exists?).returns(true)
+ File.stubs(:read).with('bin/rails').returns('ENGINE_PATH')
+ assert Rails::AppRailsLoader.in_rails_application_or_engine?
+ end
+end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 53109cb041..78aefa1437 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -192,6 +192,16 @@ module ApplicationTests
end
end
+ test "filter_parameters should be able to set via config.filter_parameters in an initializer" do
+ app_file 'config/initializers/filter_parameters_logging.rb', <<-RUBY
+ Rails.application.config.filter_parameters += [ :password, :foo, 'bar' ]
+ RUBY
+
+ require "#{app_path}/config/environment"
+
+ assert_equal [:password, :foo, 'bar'], Rails.application.env_config['action_dispatch.parameter_filter']
+ end
+
test "config.to_prepare is forwarded to ActionDispatch" do
$prepared = false
@@ -407,7 +417,17 @@ module ApplicationTests
require "#{app_path}/config/environment"
- assert_equal "Wellington", Rails.application.config.time_zone
+ 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
end
test "raises when an invalid timezone is defined in the config" do
@@ -557,6 +577,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/generators_test.rb b/railties/test/application/generators_test.rb
index bc0af499c1..78ada58ec8 100644
--- a/railties/test/application/generators_test.rb
+++ b/railties/test/application/generators_test.rb
@@ -30,7 +30,7 @@ module ApplicationTests
end
test "allow running plugin new generator inside Rails app directory" do
- FileUtils.cd(rails_root){ `ruby script/rails plugin new vendor/plugins/bukkits` }
+ FileUtils.cd(rails_root){ `ruby bin/rails plugin new vendor/plugins/bukkits` }
assert File.exist?(File.join(rails_root, "vendor/plugins/bukkits/test/dummy/config/application.rb"))
end
diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb
index c99666d7e4..b8e0c9be60 100644
--- a/railties/test/application/middleware/cache_test.rb
+++ b/railties/test/application/middleware/cache_test.rb
@@ -81,8 +81,8 @@ module ApplicationTests
add_to_config "config.action_dispatch.rack_cache = true"
get "/expires/expires_header"
- assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
- assert_equal "max-age=10, public", last_response.headers["Cache-Control"]
+ assert_equal "miss, ignore, store", last_response.headers["X-Rack-Cache"]
+ assert_equal "max-age=10, public", last_response.headers["Cache-Control"]
body = last_response.body
@@ -115,8 +115,8 @@ module ApplicationTests
add_to_config "config.action_dispatch.rack_cache = true"
get "/expires/expires_etag"
- assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
- assert_equal "public", last_response.headers["Cache-Control"]
+ assert_equal "miss, ignore, store", last_response.headers["X-Rack-Cache"]
+ assert_equal "public", last_response.headers["Cache-Control"]
body = last_response.body
etag = last_response.headers["ETag"]
@@ -149,8 +149,8 @@ module ApplicationTests
add_to_config "config.action_dispatch.rack_cache = true"
get "/expires/expires_last_modified"
- assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
- assert_equal "public", last_response.headers["Cache-Control"]
+ assert_equal "miss, ignore, store", last_response.headers["X-Rack-Cache"]
+ assert_equal "public", last_response.headers["Cache-Control"]
body = last_response.body
last = last_response.headers["Last-Modified"]
diff --git a/railties/test/application/middleware/static_test.rb b/railties/test/application/middleware/static_test.rb
new file mode 100644
index 0000000000..0a793f8f60
--- /dev/null
+++ b/railties/test/application/middleware/static_test.rb
@@ -0,0 +1,31 @@
+# encoding: utf-8
+require 'isolation/abstract_unit'
+require 'rack/test'
+
+module ApplicationTests
+ class MiddlewareStaticTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+ include Rack::Test::Methods
+
+ def setup
+ build_app
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ # Regression test to #8907
+ # See https://github.com/rails/rails/commit/9cc82b77196d21a5c7021f6dca59ab9b2b158a45#commitcomment-2416514
+ test "doesn't set Cache-Control header when it is nil" do
+ app_file "public/foo.html", 'static'
+
+ require "#{app_path}/config/environment"
+
+ get 'foo'
+
+ assert_not last_response.headers.has_key?('Cache-Control'), "Cache-Control should not be set"
+ end
+ end
+end
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
index 744bb93671..3508f4225a 100644
--- a/railties/test/application/rake/notes_test.rb
+++ b/railties/test/application/rake/notes_test.rb
@@ -62,7 +62,6 @@ module ApplicationTests
app_file "config/initializers/some_initializer.rb", "# TODO: note in config directory"
app_file "db/some_seeds.rb", "# TODO: note in db directory"
app_file "lib/some_file.rb", "# TODO: note in lib directory"
- app_file "script/run_something.rb", "# TODO: note in script directory"
app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory"
app_file "some_other_dir/blah.rb", "# TODO: note in some_other directory"
@@ -83,11 +82,10 @@ module ApplicationTests
assert_match(/note in config directory/, output)
assert_match(/note in db directory/, output)
assert_match(/note in lib directory/, output)
- assert_match(/note in script directory/, output)
assert_match(/note in test directory/, output)
assert_no_match(/note in some_other directory/, output)
- assert_equal 6, lines.size
+ assert_equal 5, lines.size
lines.each do |line_number|
assert_equal 4, line_number.size
@@ -100,7 +98,6 @@ module ApplicationTests
app_file "config/initializers/some_initializer.rb", "# TODO: note in config directory"
app_file "db/some_seeds.rb", "# TODO: note in db directory"
app_file "lib/some_file.rb", "# TODO: note in lib directory"
- app_file "script/run_something.rb", "# TODO: note in script directory"
app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory"
app_file "some_other_dir/blah.rb", "# TODO: note in some_other directory"
@@ -121,12 +118,50 @@ module ApplicationTests
assert_match(/note in config directory/, output)
assert_match(/note in db directory/, output)
assert_match(/note in lib directory/, output)
- assert_match(/note in script directory/, output)
assert_match(/note in test directory/, output)
assert_match(/note in some_other directory/, output)
- assert_equal 7, lines.size
+ assert_equal 6, lines.size
+
+ lines.each do |line_number|
+ assert_equal 4, line_number.size
+ end
+ end
+ end
+
+ test 'custom rake task finds specific notes in specific directories' do
+ app_file "app/controllers/some_controller.rb", "# TODO: note in app directory"
+ app_file "lib/some_file.rb", "# OPTIMIZE: note in lib directory\n" << "# FIXME: note in lib directory"
+ app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory"
+
+ app_file "lib/tasks/notes_custom.rake", <<-EOS
+ require 'rails/source_annotation_extractor'
+ task :notes_custom do
+ tags = 'TODO|FIXME'
+ opts = { dirs: %w(lib test), tag: true }
+ SourceAnnotationExtractor.enumerate(tags, opts)
+ end
+ EOS
+
+ boot_rails
+
+ require 'rake'
+ require 'rdoc/task'
+ require 'rake/testtask'
+
+ Rails.application.load_tasks
+
+ Dir.chdir(app_path) do
+ output = `bundle exec rake notes_custom`
+ lines = output.scan(/\[([0-9\s]+)\]/).flatten
+
+ assert_match(/\[FIXME\] note in lib directory/, output)
+ assert_match(/\[TODO\] note in test directory/, output)
+ assert_no_match(/OPTIMIZE/, output)
+ assert_no_match(/note in app directory/, output)
+
+ assert_equal 2, lines.size
lines.each do |line_number|
assert_equal 4, line_number.size
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/application/test_test.rb b/railties/test/application/test_test.rb
index f6bcaa466a..c7ad2fba8f 100644
--- a/railties/test/application/test_test.rb
+++ b/railties/test/application/test_test.rb
@@ -52,31 +52,6 @@ module ApplicationTests
run_test_file 'integration/posts_test.rb'
end
- test "performance test" do
- controller 'posts', <<-RUBY
- class PostsController < ActionController::Base
- end
- RUBY
-
- app_file 'app/views/posts/index.html.erb', <<-HTML
- Posts#index
- HTML
-
- app_file 'test/performance/posts_test.rb', <<-RUBY
- require 'test_helper'
- require 'rails/performance_test_help'
-
- class PostsTest < ActionDispatch::PerformanceTest
- def test_index
- get '/posts'
- assert_response :success
- end
- end
- RUBY
-
- run_test_file 'performance/posts_test.rb'
- end
-
private
def run_test_file(name)
result = ruby '-Itest', "#{app_path}/test/#{name}"
diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb
index 9e449856f4..6be4a5fe89 100644
--- a/railties/test/commands/console_test.rb
+++ b/railties/test/commands/console_test.rb
@@ -111,6 +111,12 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
assert_match(/\sdevelopment\s/, output)
end
+ def test_rails_env_is_dev_when_argument_is_dev_and_dev_env_is_present
+ Rails::Console.stubs(:available_environments).returns(['dev'])
+ options = Rails::Console.parse_arguments(['dev'])
+ assert_match('dev', options[:environment])
+ end
+
private
attr_reader :output
diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb
index 6316584825..38fe8ca544 100644
--- a/railties/test/commands/dbconsole_test.rb
+++ b/railties/test/commands/dbconsole_test.rb
@@ -45,6 +45,18 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
ENV['RAILS_ENV'] = "test"
end
+ def test_rails_env_is_development_when_argument_is_dev
+ Rails::DBConsole.stubs(:available_environments).returns(['development', 'test'])
+ options = Rails::DBConsole.new.send(:parse_arguments, ['dev'])
+ assert_match('development', options[:environment])
+ end
+
+ def test_rails_env_is_dev_when_argument_is_dev_and_dev_env_is_present
+ Rails::DBConsole.stubs(:available_environments).returns(['dev'])
+ options = Rails::DBConsole.new.send(:parse_arguments, ['dev'])
+ assert_match('dev', options[:environment])
+ end
+
def test_mysql
dbconsole.expects(:find_cmd_and_exec).with(%w[mysql mysql5], 'db')
start(adapter: 'mysql', database: 'db')
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 54734ed260..f8fa8ee153 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -151,7 +151,7 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_generate_should_run_script_generate_with_argument_and_options
- generator.expects(:run_ruby_script).once.with('script/rails generate model MyModel', verbose: false)
+ generator.expects(:run_ruby_script).once.with('bin/rails generate model MyModel', verbose: false)
action :generate, 'model', 'MyModel'
end
@@ -203,14 +203,14 @@ class ActionsTest < Rails::Generators::TestCase
def test_readme
run_generator
Rails::Generators::AppGenerator.expects(:source_root).times(2).returns(destination_root)
- assert_match(/Welcome to Rails/, action(:readme, "README.rdoc"))
+ assert_match "application up and running", action(:readme, "README.rdoc")
end
def test_readme_with_quiet
generator(default_arguments, quiet: true)
run_generator
Rails::Generators::AppGenerator.expects(:source_root).times(2).returns(destination_root)
- assert_no_match(/Welcome to Rails/, action(:readme, "README.rdoc"))
+ assert_no_match "application up and running", action(:readme, "README.rdoc")
end
def test_log
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index bd0f9d6a32..ee93dc49cd 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -17,23 +17,23 @@ DEFAULT_APP_FILES = %w(
app/models
app/models/concerns
app/views/layouts
+ bin/bundle
+ bin/rails
+ bin/rake
config/environments
config/initializers
config/locales
db
- doc
lib
lib/tasks
lib/assets
log
- script/rails
test/fixtures
test/controllers
test/models
test/helpers
test/mailers
test/integration
- test/performance
vendor
vendor/assets
tmp/cache
@@ -56,7 +56,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "app/views/layouts/application.html.erb", /stylesheet_link_tag\s+"application"/
assert_file "app/views/layouts/application.html.erb", /javascript_include_tag\s+"application"/
assert_file "app/assets/stylesheets/application.css"
- assert_file "config/application.rb", /config\.assets\.enabled = true/
end
def test_invalid_application_name_raises_an_error
@@ -218,14 +217,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "test/test_helper.rb" do |helper_content|
assert_no_match(/fixtures :all/, helper_content)
end
- assert_file "test/performance/browsing_test.rb"
end
def test_generator_if_skip_sprockets_is_given
run_generator [destination_root, "--skip-sprockets"]
assert_file "config/application.rb" do |content|
assert_match(/#\s+require\s+["']sprockets\/railtie["']/, content)
- assert_no_match(/config\.assets\.enabled = true/, content)
+ assert_match(/config\.assets\.enabled = false/, content)
end
assert_file "Gemfile" do |content|
assert_no_match(/sass-rails/, content)
@@ -239,8 +237,8 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_no_match(/config\.assets\.digest = true/, content)
assert_no_match(/config\.assets\.js_compressor = :uglifier/, content)
assert_no_match(/config\.assets\.css_compressor = :sass/, content)
+ assert_no_match(/config\.assets\.version = '1\.0'/, content)
end
- assert_file "test/performance/browsing_test.rb"
end
def test_inclusion_of_javascript_runtime
diff --git a/railties/test/generators/performance_test_generator_test.rb b/railties/test/generators/performance_test_generator_test.rb
deleted file mode 100644
index 37f9857193..0000000000
--- a/railties/test/generators/performance_test_generator_test.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require 'generators/generators_test_helper'
-require 'rails/generators/rails/performance_test/performance_test_generator'
-
-class PerformanceTestGeneratorTest < Rails::Generators::TestCase
- include GeneratorsTestHelper
- arguments %w(performance)
-
- def test_performance_test_skeleton_is_created
- run_generator
- assert_file "test/performance/performance_test.rb", /class PerformanceTest < ActionDispatch::PerformanceTest/
- end
-end
diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb
index ab78800a4e..4bf5a2f08b 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -209,10 +209,10 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_file "app/views"
assert_file "app/helpers"
assert_file "app/mailers"
+ assert_file "bin/rails"
assert_file "config/routes.rb", /Rails.application.routes.draw do/
assert_file "lib/bukkits/engine.rb", /module Bukkits\n class Engine < ::Rails::Engine\n end\nend/
assert_file "lib/bukkits.rb", /require "bukkits\/engine"/
- assert_file "script/rails"
end
def test_being_quiet_while_creating_dummy_application
@@ -246,15 +246,15 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
def test_usage_of_engine_commands
run_generator [destination_root, "--full"]
- assert_file "script/rails", /ENGINE_PATH = File.expand_path\('..\/..\/lib\/bukkits\/engine', __FILE__\)/
- assert_file "script/rails", /ENGINE_ROOT = File.expand_path\('..\/..', __FILE__\)/
- assert_file "script/rails", /require 'rails\/all'/
- assert_file "script/rails", /require 'rails\/engine\/commands'/
+ assert_file "bin/rails", /ENGINE_PATH = File.expand_path\('..\/..\/lib\/bukkits\/engine', __FILE__\)/
+ assert_file "bin/rails", /ENGINE_ROOT = File.expand_path\('..\/..', __FILE__\)/
+ assert_file "bin/rails", /require 'rails\/all'/
+ assert_file "bin/rails", /require 'rails\/engine\/commands'/
end
def test_shebang
run_generator [destination_root, "--full"]
- assert_file "script/rails", /#!\/usr\/bin\/env ruby/
+ assert_file "bin/rails", /#!\/usr\/bin\/env ruby/
end
def test_passing_dummy_path_as_a_parameter
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index ab00586a64..c34ce285e3 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -31,12 +31,10 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
assert_instance_method :create, content do |m|
assert_match(/@user = User\.new\(user_params\)/, m)
assert_match(/@user\.save/, m)
- assert_match(/@user\.errors/, m)
end
assert_instance_method :update, content do |m|
assert_match(/@user\.update\(user_params\)/, m)
- assert_match(/@user\.errors/, m)
end
assert_instance_method :destroy, content do |m|
@@ -127,18 +125,6 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
assert_no_file "app/views/layouts/users.html.erb"
end
- def test_skip_html_if_required
- run_generator [ "User", "name:string", "age:integer", "--no-html" ]
- assert_no_file "app/helpers/users_helper.rb"
- assert_no_file "app/views/users"
-
- assert_file "app/controllers/users_controller.rb" do |content|
- assert_no_match(/format\.html/, content)
- assert_no_match(/def edit/, content)
- assert_no_match(/def new/, content)
- end
- end
-
def test_default_orm_is_used
run_generator ["User", "--orm=unknown"]
@@ -176,7 +162,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
def test_new_hash_style
run_generator
assert_file "app/controllers/users_controller.rb" do |content|
- assert_match(/\{ render action: "new" \}/, content)
+ assert_match(/render action: 'new'/, content)
end
end
end
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 431b23b014..357f472a3f 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -41,12 +41,10 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_instance_method :create, content do |m|
assert_match(/@product_line = ProductLine\.new\(product_line_params\)/, m)
assert_match(/@product_line\.save/, m)
- assert_match(/@product_line\.errors/, m)
end
assert_instance_method :update, content do |m|
assert_match(/@product_line\.update\(product_line_params\)/, m)
- assert_match(/@product_line\.errors/, m)
end
assert_instance_method :destroy, content do |m|
@@ -158,12 +156,10 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_instance_method :create, content do |m|
assert_match(/@admin_role = Admin::Role\.new\(admin_role_params\)/, m)
assert_match(/@admin_role\.save/, m)
- assert_match(/@admin_role\.errors/, m)
end
assert_instance_method :update, content do |m|
assert_match(/@admin_role\.update\(admin_role_params\)/, m)
- assert_match(/@admin_role\.errors/, m)
end
assert_instance_method :destroy, content do |m|
@@ -257,11 +253,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_no_file "app/assets/stylesheets/posts.css"
end
- def test_scaffold_generator_no_html
- run_generator [ "posts", "--no-html" ]
- assert_no_file "app/assets/stylesheets/scaffold.css"
- end
-
def test_scaffold_generator_no_javascripts
run_generator [ "posts", "--no-javascripts" ]
assert_file "app/assets/stylesheets/scaffold.css"
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index 1e5a4545a1..d203afed5c 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -48,7 +48,7 @@ module SharedGeneratorTests
def test_options_before_application_name_raises_an_error
content = capture(:stderr){ run_generator(["--pretend", destination_root]) }
- assert_match(/Options should be given after the \w+ name. For details run: rails( plugin)? --help\n/, content)
+ assert_match(/Options should be given after the \w+ name. For details run: rails( plugin new)? --help\n/, content)
end
def test_name_collision_raises_an_error
@@ -68,12 +68,12 @@ module SharedGeneratorTests
def test_shebang_is_added_to_rails_file
run_generator [destination_root, "--ruby", "foo/bar/baz", "--full"]
- assert_file "script/rails", /#!foo\/bar\/baz/
+ assert_file "bin/rails", /#!foo\/bar\/baz/
end
def test_shebang_when_is_the_same_as_default_use_env
run_generator [destination_root, "--ruby", Thor::Util.ruby_command, "--full"]
- assert_file "script/rails", /#!\/usr\/bin\/env/
+ assert_file "bin/rails", /#!\/usr\/bin\/env/
end
def test_template_raises_an_error_with_invalid_path
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 172a42a549..7ae1b5ccfd 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -206,7 +206,7 @@ module TestHelpers
def script(script)
Dir.chdir(app_path) do
- `#{Gem.ruby} #{app_path}/script/rails #{script}`
+ `#{Gem.ruby} #{app_path}/bin/rails #{script}`
end
end
diff --git a/railties/test/rack_logger_test.rb b/railties/test/rack_logger_test.rb
new file mode 100644
index 0000000000..3a9392fd26
--- /dev/null
+++ b/railties/test/rack_logger_test.rb
@@ -0,0 +1,71 @@
+require 'active_support/testing/autorun'
+require 'active_support/test_case'
+require 'rails/rack/logger'
+require 'logger'
+
+module Rails
+ module Rack
+ class LoggerTest < ActiveSupport::TestCase
+ class TestLogger < Rails::Rack::Logger
+ NULL = ::Logger.new File::NULL
+
+ attr_reader :logger
+
+ def initialize(logger = NULL, taggers = nil, &block)
+ super(->(_) { block.call; [200, {}, []] }, taggers)
+ @logger = logger
+ end
+
+ def development?; false; end
+ end
+
+ class Subscriber < Struct.new(:starts, :finishes)
+ def initialize(starts = [], finishes = [])
+ super
+ end
+
+ def start(name, id, payload)
+ starts << [name, id, payload]
+ end
+
+ def finish(name, id, payload)
+ finishes << [name, id, payload]
+ end
+ end
+
+ attr_reader :subscriber, :notifier
+
+ def setup
+ @subscriber = Subscriber.new
+ @notifier = ActiveSupport::Notifications.notifier
+ notifier.subscribe 'action_dispatch.request', subscriber
+ end
+
+ def teardown
+ notifier.unsubscribe subscriber
+ end
+
+ def test_notification
+ logger = TestLogger.new { }
+
+ assert_difference('subscriber.starts.length') do
+ assert_difference('subscriber.finishes.length') do
+ logger.call('REQUEST_METHOD' => 'GET').last.close
+ end
+ end
+ end
+
+ def test_notification_on_raise
+ logger = TestLogger.new { raise }
+
+ assert_difference('subscriber.starts.length') do
+ assert_difference('subscriber.finishes.length') do
+ assert_raises(RuntimeError) do
+ logger.call 'REQUEST_METHOD' => 'GET'
+ end
+ end
+ end
+ end
+ end
+ end
+end
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
diff --git a/railties/test/script_rails_loader_test.rb b/railties/test/script_rails_loader_test.rb
deleted file mode 100644
index 3ccc147749..0000000000
--- a/railties/test/script_rails_loader_test.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require 'abstract_unit'
-require 'rails/script_rails_loader'
-
-class ScriptRailsLoaderTest < ActiveSupport::TestCase
-
- test "is in a rails application if script/rails exists" do
- File.stubs(:exists?).returns(true)
- assert Rails::ScriptRailsLoader.in_rails_application?
- end
-
- test "is in a rails application if parent directory has script/rails" do
- File.stubs(:exists?).with("/foo/bar/script/rails").returns(false)
- File.stubs(:exists?).with("/foo/script/rails").returns(true)
- assert Rails::ScriptRailsLoader.in_rails_application_subdirectory?(Pathname.new("/foo/bar"))
- end
-
- test "is not in a rails application if at the root directory and doesn't have script/rails" do
- Pathname.any_instance.stubs(:root?).returns true
- assert !Rails::ScriptRailsLoader.in_rails_application?
- end
-
-end \ No newline at end of file