aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--Gemfile.lock3
-rw-r--r--RELEASING_RAILS.rdoc2
-rw-r--r--actionmailer/CHANGELOG.md10
-rw-r--r--actionmailer/README.rdoc2
-rw-r--r--actionmailer/Rakefile14
-rw-r--r--actionmailer/lib/action_mailer/base.rb15
-rw-r--r--actionmailer/lib/action_mailer/delivery_job.rb2
-rw-r--r--actionmailer/lib/action_mailer/delivery_methods.rb3
-rw-r--r--actionmailer/lib/action_mailer/inline_preview_interceptor.rb2
-rw-r--r--actionmailer/lib/action_mailer/log_subscriber.rb2
-rw-r--r--actionmailer/lib/action_mailer/test_helper.rb2
-rw-r--r--actionmailer/test/abstract_unit.rb1
-rw-r--r--actionmailer/test/delivery_methods_test.rb1
-rw-r--r--actionmailer/test/message_delivery_test.rb15
-rw-r--r--actionmailer/test/test_helper_test.rb11
-rw-r--r--actionpack/CHANGELOG.md39
-rw-r--r--actionpack/README.rdoc2
-rw-r--r--actionpack/Rakefile14
-rw-r--r--actionpack/lib/action_controller.rb3
-rw-r--r--actionpack/lib/action_controller/api.rb145
-rw-r--r--actionpack/lib/action_controller/base.rb6
-rw-r--r--actionpack/lib/action_controller/metal/basic_implicit_render.rb11
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb4
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb2
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb2
-rw-r--r--actionpack/lib/action_controller/metal/implicit_render.rb9
-rw-r--r--actionpack/lib/action_controller/metal/params_wrapper.rb5
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb1
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb6
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb6
-rw-r--r--actionpack/lib/action_controller/template_assertions.rb9
-rw-r--r--actionpack/lib/action_controller/test_case.rb226
-rw-r--r--actionpack/lib/action_dispatch/http/mime_types.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb59
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb2
-rw-r--r--actionpack/lib/action_dispatch/journey/route.rb7
-rw-r--r--actionpack/lib/action_dispatch/journey/router/utils.rb2
-rw-r--r--actionpack/lib/action_dispatch/journey/routes.rb8
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb8
-rw-r--r--actionpack/lib/action_dispatch/middleware/request_id.rb13
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb12
-rw-r--r--actionpack/lib/action_dispatch/request/session.rb33
-rw-r--r--actionpack/lib/action_dispatch/routing.rb1
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb36
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb32
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb7
-rw-r--r--actionpack/lib/action_dispatch/testing/test_process.rb6
-rw-r--r--actionpack/test/abstract_unit.rb6
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb211
-rw-r--r--actionpack/test/controller/api/conditional_get_test.rb57
-rw-r--r--actionpack/test/controller/api/data_streaming_test.rb26
-rw-r--r--actionpack/test/controller/api/force_ssl_test.rb20
-rw-r--r--actionpack/test/controller/api/implicit_render_test.rb15
-rw-r--r--actionpack/test/controller/api/params_wrapper_test.rb26
-rw-r--r--actionpack/test/controller/api/redirect_to_test.rb19
-rw-r--r--actionpack/test/controller/api/renderers_test.rb38
-rw-r--r--actionpack/test/controller/api/url_for_test.rb20
-rw-r--r--actionpack/test/controller/base_test.rb4
-rw-r--r--actionpack/test/controller/caching_test.rb10
-rw-r--r--actionpack/test/controller/filters_test.rb151
-rw-r--r--actionpack/test/controller/flash_test.rb51
-rw-r--r--actionpack/test/controller/force_ssl_test.rb4
-rw-r--r--actionpack/test/controller/http_basic_authentication_test.rb3
-rw-r--r--actionpack/test/controller/http_digest_authentication_test.rb11
-rw-r--r--actionpack/test/controller/http_token_authentication_test.rb9
-rw-r--r--actionpack/test/controller/integration_test.rb2
-rw-r--r--actionpack/test/controller/log_subscriber_test.rb4
-rw-r--r--actionpack/test/controller/redirect_test.rb11
-rw-r--r--actionpack/test/controller/render_test.rb24
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb6
-rw-r--r--actionpack/test/controller/test_case_test.rb68
-rw-r--r--actionpack/test/dispatch/request_test.rb1
-rw-r--r--actionpack/test/dispatch/routing/route_set_test.rb14
-rw-r--r--actionpack/test/dispatch/routing_test.rb75
-rw-r--r--actionpack/test/dispatch/show_exceptions_test.rb2
-rw-r--r--actionpack/test/dispatch/static_test.rb14
-rw-r--r--actionpack/test/dispatch/template_assertions_test.rb110
-rw-r--r--actionpack/test/fixtures/customers/_commented_customer.html.erb1
-rw-r--r--actionpack/test/fixtures/customers/_customer.html.erb1
-rw-r--r--actionpack/test/fixtures/layouts/standard.html.erb2
-rw-r--r--actionpack/test/fixtures/public/foo/other-index.html1
-rw-r--r--actionpack/test/fixtures/public/other-index.html1
-rw-r--r--actionpack/test/fixtures/公共/foo/other-index.html1
-rw-r--r--actionpack/test/fixtures/公共/other-index.html1
-rw-r--r--actionpack/test/journey/route_test.rb26
-rw-r--r--actionpack/test/journey/router_test.rb52
-rw-r--r--actionpack/test/journey/routes_test.rb20
-rw-r--r--actionview/CHANGELOG.md12
-rw-r--r--actionview/README.rdoc2
-rw-r--r--actionview/Rakefile38
-rw-r--r--actionview/lib/action_view/helpers/atom_feed_helper.rb4
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb22
-rw-r--r--actionview/lib/action_view/helpers/date_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb1
-rw-r--r--actionview/lib/action_view/helpers/form_options_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/number_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/tags/base.rb12
-rw-r--r--actionview/lib/action_view/layouts.rb25
-rw-r--r--actionview/lib/action_view/railtie.rb6
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb2
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb2
-rw-r--r--actionview/test/actionpack/controller/layout_test.rb40
-rw-r--r--actionview/test/actionpack/controller/render_test.rb88
-rw-r--r--actionview/test/activerecord/render_partial_with_record_identification_test.rb18
-rw-r--r--actionview/test/fixtures/actionpack/layout_tests/alt/layouts/alt.erb1
-rw-r--r--actionview/test/fixtures/actionpack/test/hyphen-ated.erb2
-rw-r--r--actionview/test/fixtures/test/_partial_name_in_local_assigns.erb1
-rw-r--r--actionview/test/template/atom_feed_helper_test.rb22
-rw-r--r--actionview/test/template/form_options_helper_test.rb7
-rw-r--r--actionview/test/template/render_test.rb8
-rw-r--r--actionview/test/template/test_case_test.rb58
-rw-r--r--activejob/CHANGELOG.md9
-rw-r--r--activejob/README.md2
-rw-r--r--activejob/Rakefile15
-rw-r--r--activejob/lib/active_job/core.rb3
-rw-r--r--activejob/lib/active_job/queue_adapter.rb6
-rw-r--r--activejob/lib/active_job/queue_adapters.rb4
-rw-r--r--activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb8
-rw-r--r--activejob/lib/active_job/queue_adapters/qu_adapter.rb6
-rw-r--r--activejob/lib/active_job/queue_adapters/que_adapter.rb8
-rw-r--r--activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb4
-rw-r--r--activejob/test/cases/test_case_test.rb8
-rw-r--r--activejob/test/helper.rb2
-rw-r--r--activejob/test/integration/queuing_test.rb15
-rw-r--r--activejob/test/support/integration/test_case_helpers.rb4
-rw-r--r--activemodel/CHANGELOG.md2
-rw-r--r--activemodel/README.rdoc2
-rw-r--r--activemodel/Rakefile19
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb4
-rw-r--r--activemodel/lib/active_model/dirty.rb4
-rw-r--r--activemodel/lib/active_model/errors.rb6
-rw-r--r--activemodel/lib/active_model/serialization.rb31
-rw-r--r--activemodel/test/cases/attribute_assignment_test.rb2
-rw-r--r--activemodel/test/cases/errors_test.rb68
-rw-r--r--activemodel/test/cases/helper.rb2
-rw-r--r--activemodel/test/cases/serializers/xml_serialization_test.rb1
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb100
-rw-r--r--activemodel/test/cases/validations/with_validation_test.rb8
-rw-r--r--activerecord/CHANGELOG.md102
-rw-r--r--activerecord/README.rdoc6
-rw-r--r--activerecord/Rakefile18
-rw-r--r--activerecord/lib/active_record/aggregations.rb2
-rw-r--r--activerecord/lib/active_record/associations.rb11
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb15
-rw-r--r--activerecord/lib/active_record/attribute/user_provided_default.rb32
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb3
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb9
-rw-r--r--activerecord/lib/active_record/attributes.rb20
-rw-r--r--activerecord/lib/active_record/base.rb1
-rw-r--r--activerecord/lib/active_record/callbacks.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb479
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb23
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb28
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb77
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb37
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/column.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb50
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb40
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/schema_creation.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb85
-rw-r--r--activerecord/lib/active_record/connection_adapters/statement_pool.rb34
-rw-r--r--activerecord/lib/active_record/connection_handling.rb2
-rw-r--r--activerecord/lib/active_record/fixtures.rb8
-rw-r--r--activerecord/lib/active_record/inheritance.rb14
-rw-r--r--activerecord/lib/active_record/migration.rb18
-rw-r--r--activerecord/lib/active_record/model_schema.rb23
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb7
-rw-r--r--activerecord/lib/active_record/persistence.rb7
-rw-r--r--activerecord/lib/active_record/railtie.rb2
-rw-r--r--activerecord/lib/active_record/reflection.rb12
-rw-r--r--activerecord/lib/active_record/relation.rb3
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb4
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb22
-rw-r--r--activerecord/lib/active_record/relation/merger.rb25
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb20
-rw-r--r--activerecord/lib/active_record/sanitization.rb8
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb1
-rw-r--r--activerecord/lib/active_record/schema_migration.rb1
-rw-r--r--activerecord/lib/active_record/suppressor.rb3
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb25
-rw-r--r--activerecord/lib/active_record/transactions.rb4
-rw-r--r--activerecord/lib/active_record/type/integer.rb9
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb9
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb9
-rw-r--r--activerecord/test/cases/adapters/postgresql/geometric_test.rb119
-rw-r--r--activerecord/test/cases/adapters/sqlite3/collation_test.rb53
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb8
-rw-r--r--activerecord/test/cases/associations/eager_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb25
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb14
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb8
-rw-r--r--activerecord/test/cases/attributes_test.rb16
-rw-r--r--activerecord/test/cases/base_test.rb10
-rw-r--r--activerecord/test/cases/calculations_test.rb30
-rw-r--r--activerecord/test/cases/connection_adapters/adapter_leasing_test.rb6
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb46
-rw-r--r--activerecord/test/cases/connection_adapters/type_lookup_test.rb6
-rw-r--r--activerecord/test/cases/connection_management_test.rb23
-rw-r--r--activerecord/test/cases/connection_pool_test.rb172
-rw-r--r--activerecord/test/cases/dirty_test.rb26
-rw-r--r--activerecord/test/cases/enum_test.rb60
-rw-r--r--activerecord/test/cases/fixtures_test.rb19
-rw-r--r--activerecord/test/cases/inheritance_test.rb18
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb2
-rw-r--r--activerecord/test/cases/persistence_test.rb27
-rw-r--r--activerecord/test/cases/primary_keys_test.rb8
-rw-r--r--activerecord/test/cases/relation/delegation_test.rb2
-rw-r--r--activerecord/test/cases/relation/mutation_test.rb17
-rw-r--r--activerecord/test/cases/relation_test.rb24
-rw-r--r--activerecord/test/cases/relations_test.rb39
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb5
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb13
-rw-r--r--activerecord/test/cases/suppressor_test.rb33
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb15
-rw-r--r--activerecord/test/cases/test_case.rb2
-rw-r--r--activerecord/test/cases/type/integer_test.rb2
-rw-r--r--activerecord/test/cases/view_test.rb2
-rw-r--r--activerecord/test/fixtures/books.yml11
-rw-r--r--activerecord/test/fixtures/doubloons.yml3
-rw-r--r--activerecord/test/models/company.rb3
-rw-r--r--activerecord/test/models/doubloon.rb12
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb1
-rw-r--r--activerecord/test/schema/schema.rb5
-rw-r--r--activesupport/CHANGELOG.md64
-rw-r--r--activesupport/README.rdoc2
-rw-r--r--activesupport/Rakefile14
-rw-r--r--activesupport/lib/active_support/array_inquirer.rb14
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb2
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb9
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb2
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb2
-rw-r--r--activesupport/lib/active_support/callbacks.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/class.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/class/delegating_attributes.rb45
-rw-r--r--activesupport/lib/active_support/core_ext/date/conversions.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/numeric.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/inquiry.rb26
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb74
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/uri.rb2
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb12
-rw-r--r--activesupport/lib/active_support/deprecation/proxy_wrappers.rb7
-rw-r--r--activesupport/lib/active_support/duration.rb4
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb14
-rw-r--r--activesupport/lib/active_support/json/decoding.rb10
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb8
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_currency_converter.rb2
-rw-r--r--activesupport/lib/active_support/ordered_options.rb8
-rw-r--r--activesupport/lib/active_support/test_case.rb10
-rw-r--r--activesupport/lib/active_support/testing/assertions.rb20
-rw-r--r--activesupport/lib/active_support/testing/composite_filter.rb54
-rw-r--r--activesupport/lib/active_support/testing/time_helpers.rb2
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb1
-rw-r--r--activesupport/test/abstract_unit.rb1
-rw-r--r--activesupport/test/autoloading_fixtures/a/c/e/f.rb2
-rw-r--r--activesupport/test/autoloading_fixtures/a/c/em/f.rb2
-rw-r--r--activesupport/test/autoloading_fixtures/d.rb2
-rw-r--r--activesupport/test/autoloading_fixtures/e.rb2
-rw-r--r--activesupport/test/autoloading_fixtures/em.rb2
-rw-r--r--activesupport/test/caching_test.rb108
-rw-r--r--activesupport/test/core_ext/class/delegating_attributes_test.rb122
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb33
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb68
-rw-r--r--activesupport/test/core_ext/duration_test.rb34
-rw-r--r--activesupport/test/core_ext/enumerable_test.rb14
-rw-r--r--activesupport/test/core_ext/marshal_test.rb32
-rw-r--r--activesupport/test/core_ext/numeric_ext_test.rb84
-rw-r--r--activesupport/test/core_ext/object/try_test.rb76
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb178
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb96
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb63
-rw-r--r--activesupport/test/dependencies_test.rb30
-rw-r--r--activesupport/test/ordered_options_test.rb15
-rw-r--r--activesupport/test/time_travel_test.rb101
-rw-r--r--activesupport/test/time_zone_test.rb14
-rw-r--r--activesupport/test/xml_mini_test.rb1
-rwxr-xr-xci/travis.rb32
-rw-r--r--guides/bug_report_templates/action_controller_gem.rb15
-rw-r--r--guides/bug_report_templates/action_controller_master.rb21
-rw-r--r--guides/bug_report_templates/active_record_gem.rb16
-rw-r--r--guides/bug_report_templates/active_record_master.rb22
-rw-r--r--guides/bug_report_templates/generic_gem.rb16
-rw-r--r--guides/bug_report_templates/generic_master.rb20
-rw-r--r--guides/source/3_1_release_notes.md2
-rw-r--r--guides/source/action_mailer_basics.md6
-rw-r--r--guides/source/action_view_overview.md31
-rw-r--r--guides/source/active_record_basics.md4
-rw-r--r--guides/source/active_record_migrations.md4
-rw-r--r--guides/source/active_record_postgresql.md42
-rw-r--r--guides/source/active_record_querying.md7
-rw-r--r--guides/source/active_record_validations.md28
-rw-r--r--guides/source/active_support_core_extensions.md30
-rw-r--r--guides/source/active_support_instrumentation.md14
-rw-r--r--guides/source/api_app.md435
-rw-r--r--guides/source/association_basics.md42
-rw-r--r--guides/source/command_line.md4
-rw-r--r--guides/source/configuring.md19
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md13
-rw-r--r--guides/source/debugging_rails_applications.md11
-rw-r--r--guides/source/development_dependencies_install.md4
-rw-r--r--guides/source/documents.yaml10
-rw-r--r--guides/source/engines.md14
-rw-r--r--guides/source/form_helpers.md6
-rw-r--r--guides/source/getting_started.md8
-rw-r--r--guides/source/i18n.md5
-rw-r--r--guides/source/initialization.md13
-rw-r--r--guides/source/kindle/welcome.html.erb4
-rw-r--r--guides/source/layouts_and_rendering.md26
-rw-r--r--guides/source/nested_model_forms.md2
-rw-r--r--guides/source/rails_application_templates.md2
-rw-r--r--guides/source/routing.md7
-rw-r--r--guides/source/security.md9
-rw-r--r--guides/source/testing.md80
-rw-r--r--guides/source/upgrading_ruby_on_rails.md6
-rw-r--r--guides/source/working_with_javascript_in_rails.md28
-rw-r--r--install.rb4
-rw-r--r--railties/CHANGELOG.md26
-rw-r--r--railties/Rakefile18
-rw-r--r--railties/lib/rails.rb10
-rw-r--r--railties/lib/rails/app_loader.rb (renamed from railties/lib/rails/app_rails_loader.rb)4
-rw-r--r--railties/lib/rails/application.rb3
-rw-r--r--railties/lib/rails/application/configuration.rb13
-rw-r--r--railties/lib/rails/application/default_middleware_stack.rb8
-rw-r--r--railties/lib/rails/cli.rb4
-rw-r--r--railties/lib/rails/commands/console.rb31
-rw-r--r--railties/lib/rails/commands/console_helper.rb34
-rw-r--r--railties/lib/rails/commands/dbconsole.rb135
-rw-r--r--railties/lib/rails/commands/test.rb4
-rw-r--r--railties/lib/rails/configuration.rb3
-rw-r--r--railties/lib/rails/console/app.rb5
-rw-r--r--railties/lib/rails/engine.rb16
-rw-r--r--railties/lib/rails/generators.rb19
-rw-r--r--railties/lib/rails/generators/app_base.rb10
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb37
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile14
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/setup2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb7
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb14
-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/gitignore5
-rw-r--r--railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb1
-rw-r--r--railties/lib/rails/generators/rails/scaffold/USAGE2
-rw-r--r--railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb3
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb5
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb61
-rw-r--r--railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb6
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb15
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb40
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb5
-rw-r--r--railties/lib/rails/tasks/framework.rake34
-rw-r--r--railties/lib/rails/tasks/restart.rake1
-rw-r--r--railties/lib/rails/test_help.rb6
-rw-r--r--railties/lib/rails/test_unit/minitest_plugin.rb51
-rw-r--r--railties/lib/rails/test_unit/reporter.rb6
-rw-r--r--railties/lib/rails/test_unit/runner.rb137
-rw-r--r--railties/lib/rails/test_unit/test_requirer.rb28
-rw-r--r--railties/lib/rails/test_unit/testing.rake15
-rw-r--r--railties/test/app_loader_test.rb (renamed from railties/test/app_rails_loader_test.rb)18
-rw-r--r--railties/test/application/configuration/custom_test.rb1
-rw-r--r--railties/test/application/configuration_test.rb13
-rw-r--r--railties/test/application/console_test.rb12
-rw-r--r--railties/test/application/generators_test.rb35
-rw-r--r--railties/test/application/initializers/frameworks_test.rb29
-rw-r--r--railties/test/application/middleware/session_test.rb18
-rw-r--r--railties/test/application/middleware/static_test.rb21
-rw-r--r--railties/test/application/middleware_test.rb27
-rw-r--r--railties/test/application/multiple_applications_test.rb2
-rw-r--r--railties/test/application/rake/framework_test.rb48
-rw-r--r--railties/test/application/rake/restart_test.rb12
-rw-r--r--railties/test/application/rake_test.rb26
-rw-r--r--railties/test/application/test_runner_test.rb165
-rw-r--r--railties/test/application/test_test.rb6
-rw-r--r--railties/test/commands/dbconsole_test.rb12
-rw-r--r--railties/test/generators/actions_test.rb79
-rw-r--r--railties/test/generators/api_app_generator_test.rb96
-rw-r--r--railties/test/generators/app_generator_test.rb27
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb58
-rw-r--r--railties/test/generators/scaffold_generator_test.rb98
-rw-r--r--railties/test/generators/shared_generator_tests.rb15
-rw-r--r--railties/test/generators_test.rb52
-rw-r--r--railties/test/path_generation_test.rb2
-rw-r--r--railties/test/paths_test.rb147
-rw-r--r--railties/test/test_unit/runner_test.rb111
-rwxr-xr-xtools/profile4
395 files changed, 6095 insertions, 3400 deletions
diff --git a/.travis.yml b/.travis.yml
index 6c870d8797..2237ca85d9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,6 +19,7 @@ env:
- "GEM=ar:sqlite3"
- "GEM=ar:postgresql"
- "GEM=aj:integration"
+ - "GEM=guides"
rvm:
- 2.2.2
- ruby-head
diff --git a/Gemfile.lock b/Gemfile.lock
index 5adbf65045..3cc20f1c9c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -306,3 +306,6 @@ DEPENDENCIES
turbolinks
uglifier (>= 1.3.0)
w3c_validators
+
+BUNDLED WITH
+ 1.10.2
diff --git a/RELEASING_RAILS.rdoc b/RELEASING_RAILS.rdoc
index 0e09234b82..a020b958e1 100644
--- a/RELEASING_RAILS.rdoc
+++ b/RELEASING_RAILS.rdoc
@@ -96,7 +96,7 @@ Run `rake install` to generate the gems and install them locally. Then try
generating a new app and ensure that nothing explodes.
This will stop you from looking silly when you push an RC to rubygems.org and
-then realise it is broken.
+then realize it is broken.
=== Release the gem.
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index e2285b75e8..7c1b0b215a 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -1,3 +1,13 @@
+* Add `config.action_mailer.deliver_later_queue_name` configuration to set the
+ mailer queue name.
+
+ *Chris McGrath*
+
+* `assert_emails` in block form use the given number as expected value.
+ This makes the error message much easier to understand.
+
+ *Yuji Yaginuma*
+
* Add support for inline images in mailer previews by using an interceptor
class to convert cid: urls in image src attributes to data urls.
diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc
index a4e660d621..e5c2ed8c77 100644
--- a/actionmailer/README.rdoc
+++ b/actionmailer/README.rdoc
@@ -146,7 +146,7 @@ The Base class has the full list of configuration options. Here's an example:
The latest version of Action Mailer can be installed with RubyGems:
- % [sudo] gem install actionmailer
+ % gem install actionmailer
Source code can be downloaded as part of the Rails project on GitHub
diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile
index 8f4de8fafa..7197ea5e27 100644
--- a/actionmailer/Rakefile
+++ b/actionmailer/Rakefile
@@ -1,5 +1,4 @@
require 'rake/testtask'
-require 'rubygems/package_task'
desc "Default Task"
task default: [ :test ]
@@ -20,16 +19,3 @@ namespace :test do
end or raise "Failures"
end
end
-
-spec = eval(File.read('actionmailer.gemspec'))
-
-Gem::PackageTask.new(spec) do |p|
- p.gem_spec = spec
-end
-
-desc "Release to rubygems"
-task release: :package do
- require 'rake/gemcutter'
- Rake::Gemcutter::Tasks.new(spec).define
- Rake::Task['gem:push'].invoke
-end
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 218b7a735a..7022c04a9a 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -155,7 +155,7 @@ module ActionMailer
# Note that <tt>deliver_later</tt> will execute your method from the background job.
#
# You never instantiate your mailer class. Rather, you just call the method you defined on the class itself.
- # All instance method are expected to return a message object to be sent.
+ # All instance methods are expected to return a message object to be sent.
#
# = Multipart Emails
#
@@ -257,8 +257,8 @@ module ActionMailer
# <tt>ActionMailer::Base</tt> sets the following:
#
# * <tt>mime_version: "1.0"</tt>
- # * <tt>charset: "UTF-8",</tt>
- # * <tt>content_type: "text/plain",</tt>
+ # * <tt>charset: "UTF-8"</tt>
+ # * <tt>content_type: "text/plain"</tt>
# * <tt>parts_order: [ "text/plain", "text/enriched", "text/html" ]</tt>
#
# <tt>parts_order</tt> and <tt>charset</tt> are not actually valid <tt>Mail::Message</tt> header fields,
@@ -286,7 +286,7 @@ module ActionMailer
# end
#
# Note that the proc is evaluated right at the start of the mail message generation, so if you
- # set something in the defaults using a proc, and then set the same thing inside of your
+ # set something in the default using a proc, and then set the same thing inside of your
# mailer method, it will get over written by the mailer method.
#
# It is also possible to set these default options that will be used in all mailers through
@@ -319,8 +319,9 @@ module ActionMailer
# callbacks in the same manner that you would use callbacks in classes that
# inherit from <tt>ActionController::Base</tt>.
#
- # Note that unless you have a specific reason to do so, you should prefer using before_action
- # rather than after_action in your Action Mailer classes so that headers are parsed properly.
+ # Note that unless you have a specific reason to do so, you should prefer
+ # using <tt>before_action</tt> rather than <tt>after_action</tt> in your
+ # Action Mailer classes so that headers are parsed properly.
#
# = Previewing emails
#
@@ -412,6 +413,8 @@ module ActionMailer
#
# * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with
# <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
+ #
+ # * <tt>deliver_later_queue_name</tt> - The name of the queue used with <tt>deliver_later</tt>
class Base < AbstractController::Base
include DeliveryMethods
include Previews
diff --git a/actionmailer/lib/action_mailer/delivery_job.rb b/actionmailer/lib/action_mailer/delivery_job.rb
index e864ab7a4d..52772af2d3 100644
--- a/actionmailer/lib/action_mailer/delivery_job.rb
+++ b/actionmailer/lib/action_mailer/delivery_job.rb
@@ -4,7 +4,7 @@ module ActionMailer
# The <tt>ActionMailer::DeliveryJob</tt> class is used when you
# want to send emails outside of the request-response cycle.
class DeliveryJob < ActiveJob::Base # :nodoc:
- queue_as :mailers
+ queue_as { ActionMailer::Base.deliver_later_queue_name }
def perform(mailer, mail_method, delivery_method, *args) #:nodoc:
mailer.constantize.public_send(mail_method, *args).send(delivery_method)
diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb
index aedcd81e52..4758b55a2a 100644
--- a/actionmailer/lib/action_mailer/delivery_methods.rb
+++ b/actionmailer/lib/action_mailer/delivery_methods.rb
@@ -16,6 +16,9 @@ module ActionMailer
cattr_accessor :perform_deliveries
self.perform_deliveries = true
+ cattr_accessor :deliver_later_queue_name
+ self.deliver_later_queue_name = :mailers
+
self.delivery_methods = {}.freeze
self.delivery_method = :smtp
diff --git a/actionmailer/lib/action_mailer/inline_preview_interceptor.rb b/actionmailer/lib/action_mailer/inline_preview_interceptor.rb
index dea5e52f0c..6d02b39225 100644
--- a/actionmailer/lib/action_mailer/inline_preview_interceptor.rb
+++ b/actionmailer/lib/action_mailer/inline_preview_interceptor.rb
@@ -5,7 +5,7 @@ module ActionMailer
# that use inline cid: style urls to data: style urls so that they are visible
# when previewing a HTML email in a web browser.
#
- # This interceptor is enabled by default, to remove it delete it from the
+ # This interceptor is enabled by default. To disable it, delete it from the
# <tt>ActionMailer::Base.preview_interceptors</tt> array:
#
# ActionMailer::Base.preview_interceptors.delete(ActionMailer::InlinePreviewInterceptor)
diff --git a/actionmailer/lib/action_mailer/log_subscriber.rb b/actionmailer/lib/action_mailer/log_subscriber.rb
index 5b57c75ec3..c2f671fdac 100644
--- a/actionmailer/lib/action_mailer/log_subscriber.rb
+++ b/actionmailer/lib/action_mailer/log_subscriber.rb
@@ -2,7 +2,7 @@ require 'active_support/log_subscriber'
module ActionMailer
# Implements the ActiveSupport::LogSubscriber for logging notifications when
- # email is delivered and received.
+ # email is delivered or received.
class LogSubscriber < ActiveSupport::LogSubscriber
# An email was delivered.
def deliver(event)
diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb
index 524e6e3af1..4d03a616d2 100644
--- a/actionmailer/lib/action_mailer/test_helper.rb
+++ b/actionmailer/lib/action_mailer/test_helper.rb
@@ -34,7 +34,7 @@ module ActionMailer
original_count = ActionMailer::Base.deliveries.size
yield
new_count = ActionMailer::Base.deliveries.size
- assert_equal original_count + number, new_count, "#{number} emails expected, but #{new_count - original_count} were sent"
+ assert_equal number, new_count - original_count, "#{number} emails expected, but #{new_count - original_count} were sent"
else
assert_equal number, ActionMailer::Base.deliveries.size
end
diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb
index ae91903c95..706249a2f6 100644
--- a/actionmailer/test/abstract_unit.rb
+++ b/actionmailer/test/abstract_unit.rb
@@ -11,7 +11,6 @@ end
require 'active_support/testing/autorun'
require 'action_mailer'
require 'action_mailer/test_case'
-require 'mail'
# Emulate AV railtie
require 'action_view'
diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb
index 2e4e019bf7..78507ce7dc 100644
--- a/actionmailer/test/delivery_methods_test.rb
+++ b/actionmailer/test/delivery_methods_test.rb
@@ -1,5 +1,4 @@
require 'abstract_unit'
-require 'mail'
class MyCustomDelivery
end
diff --git a/actionmailer/test/message_delivery_test.rb b/actionmailer/test/message_delivery_test.rb
index e4dd269494..862ce26187 100644
--- a/actionmailer/test/message_delivery_test.rb
+++ b/actionmailer/test/message_delivery_test.rb
@@ -2,7 +2,6 @@ require 'abstract_unit'
require 'active_job'
require 'minitest/mock'
require 'mailers/delayed_mailer'
-require 'active_support/core_ext/numeric/time'
class MessageDeliveryTest < ActiveSupport::TestCase
include ActiveJob::TestHelper
@@ -10,6 +9,8 @@ class MessageDeliveryTest < ActiveSupport::TestCase
setup do
@previous_logger = ActiveJob::Base.logger
@previous_delivery_method = ActionMailer::Base.delivery_method
+ @previous_deliver_later_queue_name = ActionMailer::Base.deliver_later_queue_name
+ ActionMailer::Base.deliver_later_queue_name = :test_queue
ActionMailer::Base.delivery_method = :test
ActiveJob::Base.logger = Logger.new(nil)
@mail = DelayedMailer.test_message(1, 2, 3)
@@ -21,6 +22,7 @@ class MessageDeliveryTest < ActiveSupport::TestCase
teardown do
ActiveJob::Base.logger = @previous_logger
ActionMailer::Base.delivery_method = @previous_delivery_method
+ ActionMailer::Base.deliver_later_queue_name = @previous_deliver_later_queue_name
end
test 'should have a message' do
@@ -81,4 +83,15 @@ class MessageDeliveryTest < ActiveSupport::TestCase
end
end
+ test 'should enqueue the job on the correct queue' do
+ assert_performed_with(job: ActionMailer::DeliveryJob, args: ['DelayedMailer', 'test_message', 'deliver_now', 1, 2, 3], queue: "test_queue") do
+ @mail.deliver_later
+ end
+ end
+
+ test 'can override the queue when enqueuing mail' do
+ assert_performed_with(job: ActionMailer::DeliveryJob, args: ['DelayedMailer', 'test_message', 'deliver_now', 1, 2, 3], queue: "another_queue") do
+ @mail.deliver_later(queue: :another_queue)
+ end
+ end
end
diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb
index 089933e245..0a4bc75d3e 100644
--- a/actionmailer/test/test_helper_test.rb
+++ b/actionmailer/test/test_helper_test.rb
@@ -112,6 +112,17 @@ class TestHelperMailerTest < ActionMailer::TestCase
assert_match(/1 .* but 2/, error.message)
end
+ def test_assert_emails_message
+ TestHelperMailer.test.deliver_now
+ error = assert_raise ActiveSupport::TestCase::Assertion do
+ assert_emails 2 do
+ TestHelperMailer.test.deliver_now
+ end
+ end
+ assert_match "Expected: 2", error.message
+ assert_match "Actual: 1", error.message
+ end
+
def test_assert_no_emails_failure
error = assert_raise ActiveSupport::TestCase::Assertion do
assert_no_emails do
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index fbd6c5c747..7b2f150c67 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,4 +1,29 @@
-* Fix rake routes not showing the right format when
+* Add support for API only apps.
+ ActionController::API is added as a replacement of
+ ActionController::Base for this kind of applications.
+
+ *Santiago Pastorino & Jorge Bejar*
+
+* Remove `assigns` and `assert_template`. Both methods have been extracted
+ into a gem at https://github.com/rails/rails-controller-testing.
+
+ See #18950.
+
+ *Alan Guo Xiang Tan*
+
+* `FileHandler` and `Static` middleware initializers accept `index` argument
+ to configure the directory index file name. Defaults to `index` (as in
+ `index.html`).
+
+ See #20017.
+
+ *Eliot Sykes*
+
+* Deprecate `:nothing` option for `render` method.
+
+ *Mehmet Emin İNAÇ*
+
+* Fix `rake routes` not showing the right format when
nesting multiple routes.
See #18373.
@@ -50,6 +75,16 @@
*Adam Forsyth*
+* Allow `Bearer` as token-keyword in `Authorization-Header`.
+
+ Aditionally to `Token`, the keyword `Bearer` is acceptable as a keyword
+ for the auth-token. The `Bearer` keyword is described in the original
+ OAuth RFC and used in libraries like Angular-JWT.
+
+ See #19094.
+
+ *Peter Schröder*
+
* Drop request class from RouteSet constructor.
If you would like to use a custom request class, please subclass and implement
@@ -312,7 +347,7 @@
* Stop converting empty arrays in `params` to `nil`.
- This behaviour was introduced in response to CVE-2012-2660, CVE-2012-2694
+ This behavior was introduced in response to CVE-2012-2660, CVE-2012-2694
and CVE-2013-0155
ActiveRecord now issues a safe query when passing an empty array into
diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc
index 02a24a7412..44c980b070 100644
--- a/actionpack/README.rdoc
+++ b/actionpack/README.rdoc
@@ -28,7 +28,7 @@ can be used outside of Rails.
The latest version of Action Pack can be installed with RubyGems:
- % [sudo] gem install actionpack
+ % gem install actionpack
Source code can be downloaded as part of the Rails project on GitHub
diff --git a/actionpack/Rakefile b/actionpack/Rakefile
index 3bd27f8d64..601263bfac 100644
--- a/actionpack/Rakefile
+++ b/actionpack/Rakefile
@@ -1,5 +1,4 @@
require 'rake/testtask'
-require 'rubygems/package_task'
test_files = Dir.glob('test/**/*_test.rb')
@@ -24,19 +23,6 @@ namespace :test do
end
end
-spec = eval(File.read('actionpack.gemspec'))
-
-Gem::PackageTask.new(spec) do |p|
- p.gem_spec = spec
-end
-
-desc "Release to rubygems"
-task :release => :package do
- require 'rake/gemcutter'
- Rake::Gemcutter::Tasks.new(spec).define
- Rake::Task['gem:push'].invoke
-end
-
task :lines do
load File.expand_path('..', File.dirname(__FILE__)) + '/tools/line_statistics'
files = FileList["lib/**/*.rb"]
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index a1893ce920..89fc4520d3 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -7,6 +7,7 @@ require 'action_controller/metal/strong_parameters'
module ActionController
extend ActiveSupport::Autoload
+ autoload :API
autoload :Base
autoload :Caching
autoload :Metal
@@ -15,7 +16,6 @@ module ActionController
autoload :FormBuilder
autoload_under "metal" do
- autoload :Compatibility
autoload :ConditionalGet
autoload :Cookies
autoload :DataStreaming
@@ -25,6 +25,7 @@ module ActionController
autoload :Head
autoload :Helpers
autoload :HttpAuthentication
+ autoload :BasicImplicitRender
autoload :ImplicitRender
autoload :Instrumentation
autoload :MimeResponds
diff --git a/actionpack/lib/action_controller/api.rb b/actionpack/lib/action_controller/api.rb
new file mode 100644
index 0000000000..d8149e0232
--- /dev/null
+++ b/actionpack/lib/action_controller/api.rb
@@ -0,0 +1,145 @@
+require 'action_view'
+require 'action_controller'
+require 'action_controller/log_subscriber'
+
+module ActionController
+ # API Controller is a lightweight version of <tt>ActionController::Base</tt>,
+ # created for applications that don't require all functionality that a complete
+ # \Rails controller provides, allowing you to create controllers with just the
+ # features that you need for API only applications.
+ #
+ # An API Controller is different from a normal controller in the sense that
+ # by default it doesn't include a number of features that are usually required
+ # by browser access only: layouts and templates rendering, cookies, sessions,
+ # flash, assets, and so on. This makes the entire controller stack thinner,
+ # suitable for API applications. It doesn't mean you won't have such
+ # features if you need them: they're all available for you to include in
+ # your application, they're just not part of the default API Controller stack.
+ #
+ # By default, only the ApplicationController in a \Rails application inherits
+ # from <tt>ActionController::API</tt>. All other controllers in turn inherit
+ # from ApplicationController.
+ #
+ # A sample controller could look like this:
+ #
+ # class PostsController < ApplicationController
+ # def index
+ # @posts = Post.all
+ # render json: @posts
+ # end
+ # end
+ #
+ # Request, response and parameters objects all work the exact same way as
+ # <tt>ActionController::Base</tt>.
+ #
+ # == Renders
+ #
+ # The default API Controller stack includes all renderers, which means you
+ # can use <tt>render :json</tt> and brothers freely in your controllers. Keep
+ # in mind that templates are not going to be rendered, so you need to ensure
+ # your controller is calling either <tt>render</tt> or <tt>redirect</tt> in
+ # all actions, otherwise it will return 204 No Content response.
+ #
+ # def show
+ # @post = Post.find(params[:id])
+ # render json: @post
+ # end
+ #
+ # == Redirects
+ #
+ # Redirects are used to move from one action to another. You can use the
+ # <tt>redirect</tt> method in your controllers in the same way as
+ # <tt>ActionController::Base</tt>. For example:
+ #
+ # def create
+ # redirect_to root_url and return if not_authorized?
+ # # do stuff here
+ # end
+ #
+ # == Adding new behavior
+ #
+ # In some scenarios you may want to add back some functionality provided by
+ # <tt>ActionController::Base</tt> that is not present by default in
+ # <tt>ActionController::API</tt>, for instance <tt>MimeResponds</tt>. This
+ # module gives you the <tt>respond_to</tt> and <tt>respond_with</tt> methods.
+ # Adding it is quite simple, you just need to include the module in a specific
+ # controller or in <tt>ApplicationController</tt> in case you want it
+ # available to your entire app:
+ #
+ # class ApplicationController < ActionController::API
+ # include ActionController::MimeResponds
+ # end
+ #
+ # class PostsController < ApplicationController
+ # def index
+ # @posts = Post.all
+ #
+ # respond_to do |format|
+ # format.json { render json: @posts }
+ # format.xml { render xml: @posts }
+ # end
+ # end
+ # end
+ #
+ # Quite straightforward. Make sure to check <tt>ActionController::Base</tt>
+ # available modules if you want to include any other functionality that is
+ # not provided by <tt>ActionController::API</tt> out of the box.
+ class API < Metal
+ abstract!
+
+ # Shortcut helper that returns all the ActionController::API modules except the ones passed in the argument:
+ #
+ # class MetalController
+ # ActionController::API.without_modules(:Redirecting, :DataStreaming).each do |left|
+ # include left
+ # end
+ # end
+ #
+ # This gives better control over what you want to exclude and makes it easier
+ # to create an api controller class, instead of listing the modules required manually.
+ def self.without_modules(*modules)
+ modules = modules.map do |m|
+ m.is_a?(Symbol) ? ActionController.const_get(m) : m
+ end
+
+ MODULES - modules
+ end
+
+ MODULES = [
+ AbstractController::Rendering,
+
+ UrlFor,
+ Redirecting,
+ Rendering,
+ Renderers::All,
+ ConditionalGet,
+ RackDelegation,
+ BasicImplicitRender,
+ StrongParameters,
+
+ ForceSSL,
+ DataStreaming,
+
+ # Before callbacks should also be executed the earliest as possible, so
+ # also include them at the bottom.
+ AbstractController::Callbacks,
+
+ # Append rescue at the bottom to wrap as much as possible.
+ Rescue,
+
+ # Add instrumentations hooks at the bottom, to ensure they instrument
+ # all the methods properly.
+ Instrumentation,
+
+ # Params wrapper should come before instrumentation so they are
+ # properly showed in logs
+ ParamsWrapper
+ ]
+
+ MODULES.each do |mod|
+ include mod
+ end
+
+ ActiveSupport.run_load_hooks(:action_controller, self)
+ end
+end
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index bfae372f53..2c3b3f4e05 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -50,9 +50,9 @@ module ActionController
#
# == Parameters
#
- # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
- # which returns a hash. For example, an action that was performed through <tt>/posts?category=All&limit=5</tt> will include
- # <tt>{ "category" => "All", "limit" => "5" }</tt> in params.
+ # All request parameters, whether they come from a query string in the URL or form data submitted through a POST request are
+ # available through the params method which returns a hash. For example, an action that was performed through
+ # <tt>/posts?category=All&limit=5</tt> will include <tt>{ "category" => "All", "limit" => "5" }</tt> in params.
#
# It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
#
diff --git a/actionpack/lib/action_controller/metal/basic_implicit_render.rb b/actionpack/lib/action_controller/metal/basic_implicit_render.rb
new file mode 100644
index 0000000000..6c6f8381ff
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/basic_implicit_render.rb
@@ -0,0 +1,11 @@
+module ActionController
+ module BasicImplicitRender
+ def send_action(method, *args)
+ super.tap { default_render unless performed? }
+ end
+
+ def default_render(*args)
+ head :no_content
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 47bcfdb1e9..bb3ad9b850 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -40,7 +40,7 @@ module ActionController
# * <tt>:etag</tt>.
# * <tt>:last_modified</tt>.
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
- # +true+ if you want your application to be cachable by other devices (proxy caches).
+ # +true+ if you want your application to be cacheable by other devices (proxy caches).
# * <tt>:template</tt> By default, the template digest for the current
# controller/action is included in ETags. If the action renders a
# different template, you can include its digest instead. If the action
@@ -111,7 +111,7 @@ module ActionController
# * <tt>:etag</tt>.
# * <tt>:last_modified</tt>.
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
- # +true+ if you want your application to be cachable by other devices (proxy caches).
+ # +true+ if you want your application to be cacheable by other devices (proxy caches).
# * <tt>:template</tt> By default, the template digest for the current
# controller/action is included in ETags. If the action renders a
# different template, you can include its digest instead. If the action
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index 4038101fe0..b4da381d26 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -44,7 +44,7 @@ module ActionController
# the output might look like this:
#
# 23 Aug 11:30 | Carolina Railhawks Soccer Match
- # N/A | Carolina Railhaws Training Workshop
+ # N/A | Carolina Railhawks Training Workshop
#
module Helpers
extend ActiveSupport::Concern
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 32c3c9652f..fb0a52b076 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -397,7 +397,7 @@ module ActionController
# RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
module Token
TOKEN_KEY = 'token='
- TOKEN_REGEX = /^Token /
+ TOKEN_REGEX = /^(Token|Bearer) /
AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
extend self
diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb
index 1573ea7099..d66b2214ce 100644
--- a/actionpack/lib/action_controller/metal/implicit_render.rb
+++ b/actionpack/lib/action_controller/metal/implicit_render.rb
@@ -1,17 +1,14 @@
module ActionController
module ImplicitRender
- def send_action(method, *args)
- ret = super
- default_render unless performed?
- ret
- end
+
+ include BasicImplicitRender
def default_render(*args)
if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
render(*args)
else
logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
- head :no_content
+ super
end
end
diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb
index 0a04848eba..8a4ea70649 100644
--- a/actionpack/lib/action_controller/metal/params_wrapper.rb
+++ b/actionpack/lib/action_controller/metal/params_wrapper.rb
@@ -8,8 +8,7 @@ module ActionController
# POST requests without having to specify any root elements.
#
# This functionality is enabled in +config/initializers/wrap_parameters.rb+
- # and can be customized. If you are upgrading to \Rails 3.1, this file will
- # need to be created for the functionality to be enabled.
+ # and can be customized.
#
# You could also turn it on per controller by setting the format array to
# a non-empty array:
@@ -251,7 +250,7 @@ module ActionController
private
- # Returns the wrapper key which will be used to stored wrapped parameters.
+ # Returns the wrapper key which will be used to store wrapped parameters.
def _wrapper_key
_wrapper_options.name
end
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 2d15c39d88..b9ae8dd5ea 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -79,6 +79,7 @@ module ActionController
end
if options.delete(:nothing)
+ ActiveSupport::Deprecation.warn("`:nothing` option is deprecated and will be removed in Rails 5.1. Use `head` method to respond with empty response body.")
options[:body] = nil
end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 31c8856437..4cb634477e 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -19,7 +19,7 @@ module ActionController #:nodoc:
#
# Since HTML and JavaScript requests are typically made from the browser, we
# need to ensure to verify request authenticity for the web browser. We can
- # use session-oriented authentication for these types requests, by using
+ # use session-oriented authentication for these types of requests, by using
# the `protect_form_forgery` method in our controllers.
#
# GET requests are not protected since they don't have side effects like writing
@@ -44,7 +44,7 @@ module ActionController #:nodoc:
# during request.
#
# We may want to disable CSRF protection for APIs since they are typically
- # designed to be state-less. That is, the requestion API client will handle
+ # designed to be state-less. That is, the request API client will handle
# the session for you instead of Rails.
#
# The token parameter is named <tt>authenticity_token</tt> by default. The name and
@@ -258,7 +258,7 @@ module ActionController #:nodoc:
# Returns true or false if a request is verified. Checks:
#
- # * is it a GET or HEAD request? Gets should be safe and idempotent
+ # * Is it a GET or HEAD request? Gets should be safe and idempotent
# * Does the form_authenticity_token match the given token value from the params?
# * Does the X-CSRF-Token header match the form_authenticity_token
def verified_request?
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 04401cad7b..af31de1f3a 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -110,9 +110,9 @@ module ActionController #:nodoc:
# This means that, if you have <code>yield :title</code> in your layout
# and you want to use streaming, you would have to render the whole template
# (and eventually trigger all queries) before streaming the title and all
- # assets, which kills the purpose of streaming. For this reason Rails 3.1
- # introduces a new helper called +provide+ that does the same as +content_for+
- # but tells the layout to stop searching for other entries and continue rendering.
+ # assets, which kills the purpose of streaming. For this purpose, you can use
+ # a helper called +provide+ that does the same as +content_for+ but tells the
+ # layout to stop searching for other entries and continue rendering.
#
# For instance, the template above using +provide+ would be:
#
diff --git a/actionpack/lib/action_controller/template_assertions.rb b/actionpack/lib/action_controller/template_assertions.rb
new file mode 100644
index 0000000000..0179f4afcd
--- /dev/null
+++ b/actionpack/lib/action_controller/template_assertions.rb
@@ -0,0 +1,9 @@
+module ActionController
+ module TemplateAssertions
+ def assert_template(options = {}, message = nil)
+ raise NoMethodError,
+ "assert_template has been extracted to a gem. To continue using it,
+ add `gem 'rails-controller-testing'` to your Gemfile."
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index ca7ba90c40..d34149143f 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -2,197 +2,10 @@ require 'rack/session/abstract/id'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/module/anonymous'
require 'active_support/core_ext/hash/keys'
-
+require 'action_controller/template_assertions'
require 'rails-dom-testing'
module ActionController
- module TemplateAssertions
- extend ActiveSupport::Concern
-
- included do
- setup :setup_subscriptions
- teardown :teardown_subscriptions
- end
-
- RENDER_TEMPLATE_INSTANCE_VARIABLES = %w{partials templates layouts files}.freeze
-
- def setup_subscriptions
- RENDER_TEMPLATE_INSTANCE_VARIABLES.each do |instance_variable|
- instance_variable_set("@_#{instance_variable}", Hash.new(0))
- end
-
- @_subscribers = []
-
- @_subscribers << ActiveSupport::Notifications.subscribe("render_template.action_view") do |_name, _start, _finish, _id, payload|
- path = payload[:layout]
- if path
- @_layouts[path] += 1
- if path =~ /^layouts\/(.*)/
- @_layouts[$1] += 1
- end
- end
- end
-
- @_subscribers << ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload|
- if virtual_path = payload[:virtual_path]
- partial = virtual_path =~ /^.*\/_[^\/]*$/
-
- if partial
- @_partials[virtual_path] += 1
- @_partials[virtual_path.split("/").last] += 1
- end
-
- @_templates[virtual_path] += 1
- else
- path = payload[:identifier]
- if path
- @_files[path] += 1
- @_files[path.split("/").last] += 1
- end
- end
- end
- end
-
- def teardown_subscriptions
- @_subscribers.each do |subscriber|
- ActiveSupport::Notifications.unsubscribe(subscriber)
- end
- end
-
- def process(*args)
- reset_template_assertion
- super
- end
-
- def reset_template_assertion
- RENDER_TEMPLATE_INSTANCE_VARIABLES.each do |instance_variable|
- ivar_name = "@_#{instance_variable}"
- if instance_variable_defined?(ivar_name)
- instance_variable_get(ivar_name).clear
- end
- end
- end
-
- # Asserts that the request was rendered with the appropriate template file or partials.
- #
- # # assert that the "new" view template was rendered
- # assert_template "new"
- #
- # # assert that the exact template "admin/posts/new" was rendered
- # assert_template %r{\Aadmin/posts/new\Z}
- #
- # # assert that the layout 'admin' was rendered
- # assert_template layout: 'admin'
- # assert_template layout: 'layouts/admin'
- # assert_template layout: :admin
- #
- # # assert that no layout was rendered
- # assert_template layout: nil
- # assert_template layout: false
- #
- # # assert that the "_customer" partial was rendered twice
- # assert_template partial: '_customer', count: 2
- #
- # # assert that no partials were rendered
- # assert_template partial: false
- #
- # # assert that a file was rendered
- # assert_template file: "README.rdoc"
- #
- # # assert that no file was rendered
- # assert_template file: nil
- # assert_template file: false
- #
- # In a view test case, you can also assert that specific locals are passed
- # to partials:
- #
- # # 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.
- response.body
-
- case options
- when NilClass, Regexp, String, Symbol
- options = options.to_s if Symbol === options
- rendered = @_templates
- msg = message || sprintf("expecting <%s> but rendering with <%s>",
- options.inspect, rendered.keys)
- matches_template =
- case options
- when String
- !options.empty? && rendered.any? do |t, num|
- options_splited = options.split(File::SEPARATOR)
- t_splited = t.split(File::SEPARATOR)
- t_splited.last(options_splited.size) == options_splited
- end
- when Regexp
- rendered.any? { |t,num| t.match(options) }
- when NilClass
- rendered.blank?
- end
- assert matches_template, msg
- when Hash
- options.assert_valid_keys(:layout, :partial, :locals, :count, :file)
-
- if options.key?(:layout)
- expected_layout = options[:layout]
- msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
- expected_layout, @_layouts.keys)
-
- case expected_layout
- when String, Symbol
- assert_includes @_layouts.keys, expected_layout.to_s, msg
- when Regexp
- assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg)
- when nil, false
- assert(@_layouts.empty?, msg)
- else
- raise ArgumentError, "assert_template only accepts a String, Symbol, Regexp, nil or false for :layout"
- end
- end
-
- if options[:file]
- assert_includes @_files.keys, options[:file]
- elsif options.key?(:file)
- assert @_files.blank?, "expected no files but #{@_files.keys} was rendered"
- end
-
- if expected_partial = options[:partial]
- if expected_locals = options[:locals]
- if defined?(@_rendered_views)
- view = expected_partial.to_s.sub(/^_/, '').sub(/\/_(?=[^\/]+\z)/, '/')
-
- partial_was_not_rendered_msg = "expected %s to be rendered but it was not." % view
- assert_includes @_rendered_views.rendered_views, view, partial_was_not_rendered_msg
-
- msg = 'expecting %s to be rendered with %s but was with %s' % [expected_partial,
- expected_locals,
- @_rendered_views.locals_for(view)]
- assert(@_rendered_views.view_rendered?(view, options[:locals]), msg)
- else
- warn "the :locals option to #assert_template is only supported in a ActionView::TestCase"
- end
- elsif expected_count = options[:count]
- actual_count = @_partials[expected_partial]
- msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
- expected_partial, expected_count, actual_count)
- assert(actual_count == expected_count.to_i, msg)
- else
- msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
- options[:partial], @_partials.keys)
- assert_includes @_partials, expected_partial, msg
- end
- elsif options.key?(:partial)
- assert @_partials.empty?,
- "Expected no partials to be rendered"
- end
- else
- raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil"
- end
- end
- end
-
class TestRequest < ActionDispatch::TestRequest #:nodoc:
DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
DEFAULT_ENV.delete 'PATH_INFO'
@@ -205,9 +18,10 @@ module ActionController
end
def assign_parameters(routes, controller_path, action, parameters = {})
- parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
- extra_keys = routes.extra_keys(parameters)
+ parameters = parameters.symbolize_keys
+ extra_keys = routes.extra_keys(parameters.merge(:controller => controller_path, :action => action))
non_path_parameters = get? ? query_parameters : request_parameters
+
parameters.each do |key, value|
if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?))
value = value.map{ |v| v.duplicable? ? v.dup : v }
@@ -217,7 +31,7 @@ module ActionController
value = value.dup
end
- if extra_keys.include?(key)
+ if extra_keys.include?(key) || key == :action || key == :controller
non_path_parameters[key] = value
else
if value.is_a?(Array)
@@ -230,19 +44,16 @@ module ActionController
end
end
+ path_parameters[:controller] = controller_path
+ path_parameters[:action] = action
+
# Clear the combined params hash in case it was already referenced.
@env.delete("action_dispatch.request.parameters")
# Clear the filter cache variables so they're not stale
@filtered_parameters = @filtered_env = @filtered_path = nil
- params = self.request_parameters.dup
- %w(controller action only_path).each do |k|
- params.delete(k)
- params.delete(k.to_sym)
- end
- data = params.to_query
-
+ data = request_parameters.to_query
@env['CONTENT_LENGTH'] = data.length.to_s
@env['rack.input'] = StringIO.new(data)
end
@@ -407,21 +218,15 @@ module ActionController
# In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
# can be used against. These collections are:
#
- # * assigns: Instance variables assigned in the action that are available for the view.
# * session: Objects being saved in the session.
# * flash: The flash objects currently in the session.
# * cookies: \Cookies being sent to the user on this request.
#
# These collections can be used just like any other hash:
#
- # assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
# assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
# assert flash.empty? # makes sure that there's nothing in the flash
#
- # For historic reasons, the assigns hash uses string-based keys. So <tt>assigns[:person]</tt> won't work, but <tt>assigns["person"]</tt> will. To
- # appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing.
- # So <tt>assigns(:person)</tt> will work just like <tt>assigns["person"]</tt>, but again, <tt>assigns[:person]</tt> will not work.
- #
# On top of the collections, you have the complete url that a given action redirected to available in <tt>redirect_to_url</tt>.
#
# For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
@@ -669,12 +474,10 @@ module ActionController
@controller.request = @request
@controller.response = @response
- build_request_uri(action, parameters)
-
- name = @request.parameters[:action]
+ build_request_uri(controller_class_name, action, parameters)
@controller.recycle!
- @controller.process(name)
+ @controller.process(action)
if cookies = @request.env['action_dispatch.cookies']
unless @response.committed?
@@ -683,8 +486,6 @@ module ActionController
end
@response.prepare!
- @assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
-
if flash_value = @request.flash.to_session_value
@request.session['flash'] = flash_value
else
@@ -749,7 +550,7 @@ module ActionController
args.first.merge!(method: http_method)
process(action, *args)
else
- non_kwarg_request_warning if args.present?
+ non_kwarg_request_warning if args.any?
args = args.unshift(http_method)
process(action, *args)
@@ -790,10 +591,11 @@ module ActionController
end
end
- def build_request_uri(action, parameters)
+ def build_request_uri(controller_class_name, action, parameters)
unless @request.env["PATH_INFO"]
options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters
options.update(
+ :controller => controller_class_name,
:action => action,
:relative_url_root => nil,
:_recall => @request.path_parameters)
diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb
index 0e4da36038..01a10c693b 100644
--- a/actionpack/lib/action_dispatch/http/mime_types.rb
+++ b/actionpack/lib/action_dispatch/http/mime_types.rb
@@ -27,7 +27,7 @@ Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
# http://www.ietf.org/rfc/rfc4627.txt
# http://www.json.org/JSONRequest.html
-Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
+Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest application/vnd.api+json )
Mime::Type.register "application/pdf", :pdf, [], %w(pdf)
Mime::Type.register "application/zip", :zip, [], %w(zip)
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index a1f84e5ace..3c62c055e5 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -20,6 +20,8 @@ module ActionDispatch
include ActionDispatch::Http::FilterParameters
include ActionDispatch::Http::URL
+ HTTP_X_REQUEST_ID = "HTTP_X_REQUEST_ID".freeze # :nodoc:
+
autoload :Session, 'action_dispatch/request/session'
autoload :Utils, 'action_dispatch/request/utils'
@@ -50,7 +52,6 @@ module ActionDispatch
@original_fullpath = nil
@fullpath = nil
@ip = nil
- @request_id = nil
end
def check_path_parameters!
@@ -140,47 +141,11 @@ module ActionDispatch
HTTP_METHOD_LOOKUP[method]
end
- # Is this a GET (or HEAD) request?
- # Equivalent to <tt>request.request_method_symbol == :get</tt>.
- def get?
- HTTP_METHOD_LOOKUP[request_method] == :get
- end
-
- # Is this a POST request?
- # Equivalent to <tt>request.request_method_symbol == :post</tt>.
- def post?
- HTTP_METHOD_LOOKUP[request_method] == :post
- end
-
- # Is this a PATCH request?
- # Equivalent to <tt>request.request_method == :patch</tt>.
- def patch?
- HTTP_METHOD_LOOKUP[request_method] == :patch
- end
-
- # Is this a PUT request?
- # Equivalent to <tt>request.request_method_symbol == :put</tt>.
- def put?
- HTTP_METHOD_LOOKUP[request_method] == :put
- end
-
- # Is this a DELETE request?
- # Equivalent to <tt>request.request_method_symbol == :delete</tt>.
- def delete?
- HTTP_METHOD_LOOKUP[request_method] == :delete
- end
-
- # Is this a HEAD request?
- # Equivalent to <tt>request.request_method_symbol == :head</tt>.
- def head?
- HTTP_METHOD_LOOKUP[request_method] == :head
- end
-
# Provides access to the request's HTTP headers, for example:
#
# request.headers["Content-Type"] # => "text/plain"
def headers
- Http::Headers.new(@env)
+ @headers ||= Http::Headers.new(@env)
end
# Returns a +String+ with the last requested path including their params.
@@ -234,15 +199,19 @@ module ActionDispatch
end
alias :xhr? :xml_http_request?
+ # Returns the IP address of client as a +String+.
def ip
@ip ||= super
end
- # Originating IP address, usually set by the RemoteIp middleware.
+ # Returns the IP address of client as a +String+,
+ # usually set by the RemoteIp middleware.
def remote_ip
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
end
+ ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc:
+
# Returns the unique request id, which is based on either the X-Request-Id header that can
# be generated by a firewall, load balancer, or web server or by the RequestId middleware
# (which sets the action_dispatch.request_id environment variable).
@@ -250,11 +219,19 @@ module ActionDispatch
# This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
# This relies on the rack variable set by the ActionDispatch::RequestId middleware.
def request_id
- @request_id ||= env["action_dispatch.request_id"]
+ env[ACTION_DISPATCH_REQUEST_ID]
+ end
+
+ def request_id=(id) # :nodoc:
+ env[ACTION_DISPATCH_REQUEST_ID] = id
end
alias_method :uuid, :request_id
+ def x_request_id # :nodoc:
+ @env[HTTP_X_REQUEST_ID]
+ end
+
# Returns the lowercase name of the HTTP server software.
def server_software
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
@@ -282,6 +259,8 @@ module ActionDispatch
end
end
+ # Returns true if the request's content MIME type is
+ # +application/x-www-form-urlencoded+ or +multipart/form-data+.
def form_data?
FORM_DATA_MEDIA_TYPES.include?(content_mime_type.to_s)
end
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index a895d1ab18..9e53a0f08b 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -274,7 +274,7 @@ module ActionDispatch # :nodoc:
end
# Turns the Response into a Rack-compatible array of the status, headers,
- # and body. Allows explict splatting:
+ # and body. Allows explicit splatting:
#
# status, headers, body = *response
def to_a
diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb
index 4698ff8cc7..cbc985640a 100644
--- a/actionpack/lib/action_dispatch/journey/route.rb
+++ b/actionpack/lib/action_dispatch/journey/route.rb
@@ -11,7 +11,7 @@ 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 = {})
+ def initialize(name, app, path, constraints, required_defaults, defaults)
@name = name
@app = app
@path = path
@@ -19,6 +19,7 @@ module ActionDispatch
@constraints = constraints
@defaults = defaults
@required_defaults = nil
+ @_required_defaults = required_defaults || []
@required_parts = nil
@parts = nil
@decorated_ast = nil
@@ -73,7 +74,7 @@ module ActionDispatch
end
def required_default?(key)
- (constraints[:required_defaults] || []).include?(key)
+ @_required_defaults.include?(key)
end
def required_defaults
@@ -92,8 +93,6 @@ module ActionDispatch
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
diff --git a/actionpack/lib/action_dispatch/journey/router/utils.rb b/actionpack/lib/action_dispatch/journey/router/utils.rb
index 2b0a6575d4..d02ed96d0d 100644
--- a/actionpack/lib/action_dispatch/journey/router/utils.rb
+++ b/actionpack/lib/action_dispatch/journey/router/utils.rb
@@ -55,7 +55,7 @@ module ActionDispatch
def unescape_uri(uri)
encoding = uri.encoding == US_ASCII ? UTF_8 : uri.encoding
- uri.gsub(ESCAPED) { [$&[1, 2].hex].pack('C') }.force_encoding(encoding)
+ uri.gsub(ESCAPED) { |match| [match[1, 2].hex].pack('C') }.force_encoding(encoding)
end
protected
diff --git a/actionpack/lib/action_dispatch/journey/routes.rb b/actionpack/lib/action_dispatch/journey/routes.rb
index a6d1980db2..5990964b57 100644
--- a/actionpack/lib/action_dispatch/journey/routes.rb
+++ b/actionpack/lib/action_dispatch/journey/routes.rb
@@ -16,6 +16,10 @@ module ActionDispatch
@simulator = nil
end
+ def empty?
+ routes.empty?
+ end
+
def length
routes.length
end
@@ -59,8 +63,8 @@ module ActionDispatch
end
# Add a route to the routing table.
- def add_route(app, path, conditions, defaults, name = nil)
- route = Route.new(name, app, path, conditions, defaults)
+ def add_route(app, path, conditions, required_defaults, defaults, name = nil)
+ route = Route.new(name, app, path, conditions, required_defaults, defaults)
route.precedence = routes.length
routes << route
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index 7c4236518d..9f894e2ec6 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -74,7 +74,7 @@ module ActionDispatch
# requests. For those requests that do need to know the IP, the
# GetIp#calculate_ip method will calculate the memoized client IP address.
def call(env)
- env["action_dispatch.remote_ip"] = GetIp.new(env, self)
+ env["action_dispatch.remote_ip"] = GetIp.new(env, check_ip, proxies)
@app.call(env)
end
@@ -82,10 +82,10 @@ module ActionDispatch
# into an actual IP address. If the ActionDispatch::Request#remote_ip method
# is called, this class will calculate the value and then memoize it.
class GetIp
- def initialize(env, middleware)
+ def initialize(env, check_ip, proxies)
@env = env
- @check_ip = middleware.check_ip
- @proxies = middleware.proxies
+ @check_ip = check_ip
+ @proxies = proxies
end
# Sort through the various IP address headers, looking for the IP most
diff --git a/actionpack/lib/action_dispatch/middleware/request_id.rb b/actionpack/lib/action_dispatch/middleware/request_id.rb
index b9ca524309..1555ff72af 100644
--- a/actionpack/lib/action_dispatch/middleware/request_id.rb
+++ b/actionpack/lib/action_dispatch/middleware/request_id.rb
@@ -13,22 +13,23 @@ module ActionDispatch
# from multiple pieces of the stack.
class RequestId
X_REQUEST_ID = "X-Request-Id".freeze # :nodoc:
- ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc:
- HTTP_X_REQUEST_ID = "HTTP_X_REQUEST_ID".freeze # :nodoc:
def initialize(app)
@app = app
end
def call(env)
- env[ACTION_DISPATCH_REQUEST_ID] = external_request_id(env) || internal_request_id
- @app.call(env).tap { |_status, headers, _body| headers[X_REQUEST_ID] = env[ACTION_DISPATCH_REQUEST_ID] }
+ req = ActionDispatch::Request.new env
+ req.request_id = make_request_id(req.x_request_id)
+ @app.call(env).tap { |_status, headers, _body| headers[X_REQUEST_ID] = req.request_id }
end
private
- def external_request_id(env)
- if request_id = env[HTTP_X_REQUEST_ID].presence
+ def make_request_id(request_id)
+ if request_id.presence
request_id.gsub(/[^\w\-]/, "".freeze).first(255)
+ else
+ internal_request_id
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index bc5ef1abc9..b098ea389f 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -13,14 +13,14 @@ module ActionDispatch
# located at `public/assets/application.js` if the file exists. If the file
# does not exist, a 404 "File not Found" response will be returned.
class FileHandler
- def initialize(root, cache_control)
+ def initialize(root, cache_control, index: 'index')
@root = root.chomp('/')
@compiled_root = /^#{Regexp.escape(root)}/
headers = cache_control && { 'Cache-Control' => cache_control }
@file_server = ::Rack::File.new(@root, headers)
+ @index = index
end
-
# Takes a path to a file. If the file is found, has valid encoding, and has
# correct read permissions, the return value is a URI-escaped string
# representing the filename. Otherwise, false is returned.
@@ -32,7 +32,7 @@ module ActionDispatch
return false unless path.valid_encoding?
path = Rack::Utils.clean_path_info path
- paths = [path, "#{path}#{ext}", "#{path}/index#{ext}"]
+ paths = [path, "#{path}#{ext}", "#{path}/#{@index}#{ext}"]
if match = paths.detect { |p|
path = File.join(@root, p.force_encoding('UTF-8'))
@@ -104,9 +104,9 @@ module ActionDispatch
# produce a directory traversal using this middleware. Only 'GET' and 'HEAD'
# requests will result in a file being returned.
class Static
- def initialize(app, path, cache_control=nil)
+ def initialize(app, path, cache_control = nil, index: 'index')
@app = app
- @file_handler = FileHandler.new(path, cache_control)
+ @file_handler = FileHandler.new(path, cache_control, index: index)
end
def call(env)
@@ -114,7 +114,7 @@ module ActionDispatch
when 'GET', 'HEAD'
path = env['PATH_INFO'].chomp('/')
if match = @file_handler.match?(path)
- env["PATH_INFO"] = match
+ env['PATH_INFO'] = match
return @file_handler.call(env)
end
end
diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb
index 9a1a05e971..773d41de88 100644
--- a/actionpack/lib/action_dispatch/request/session.rb
+++ b/actionpack/lib/action_dispatch/request/session.rb
@@ -86,11 +86,14 @@ module ActionDispatch
load_for_write!
end
+ # Returns value of the key stored in the session or
+ # nil if the given key is not found in the session.
def [](key)
load_for_read!
@delegate[key.to_s]
end
+ # Returns true if the session has the given key or false.
def has_key?(key)
load_for_read!
@delegate.key?(key.to_s)
@@ -98,39 +101,69 @@ module ActionDispatch
alias :key? :has_key?
alias :include? :has_key?
+ # Returns keys of the session as Array.
def keys
@delegate.keys
end
+ # Returns values of the session as Array.
def values
@delegate.values
end
+ # Writes given value to given key of the session.
def []=(key, value)
load_for_write!
@delegate[key.to_s] = value
end
+ # Clears the session.
def clear
load_for_write!
@delegate.clear
end
+ # Returns the session as Hash.
def to_hash
load_for_read!
@delegate.dup.delete_if { |_,v| v.nil? }
end
+ # Updates the session with given Hash.
+ #
+ # session.to_hash
+ # # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2"}
+ #
+ # session.update({ "foo" => "bar" })
+ # # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
+ #
+ # session.to_hash
+ # # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
def update(hash)
load_for_write!
@delegate.update stringify_keys(hash)
end
+ # Deletes given key from the session.
def delete(key)
load_for_write!
@delegate.delete key.to_s
end
+ # Returns value of given key from the session, or raises +KeyError+
+ # if can't find given key in case of not setted dafault value.
+ # Returns default value if specified.
+ #
+ # session.fetch(:foo)
+ # # => KeyError: key not found: "foo"
+ #
+ # session.fetch(:foo, :bar)
+ # # => :bar
+ #
+ # session.fetch(:foo) do
+ # :bar
+ # end
+ # # => :bar
def fetch(key, default=Unspecified, &block)
load_for_read!
if default == Unspecified
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index ce03164ca9..a42cf72f60 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -232,7 +232,6 @@ module ActionDispatch
# def send_to_jail
# get '/jail'
# assert_response :success
- # assert_template "jail/front"
# end
#
# def goes_to_login
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 0a444ddffc..7cfe4693c1 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -806,7 +806,7 @@ module ActionDispatch
# Scopes routes to a specific controller
#
# controller "food" do
- # match "bacon", action: "bacon"
+ # match "bacon", action: :bacon, via: :get
# end
def controller(controller, options={})
options[:controller] = controller
@@ -1042,7 +1042,7 @@ module ActionDispatch
class Resource #:nodoc:
attr_reader :controller, :path, :options, :param
- def initialize(entities, options = {})
+ def initialize(entities, api_only = false, options = {})
@name = entities.to_s
@path = (options[:path] || @name).to_s
@controller = (options[:controller] || @name).to_s
@@ -1050,10 +1050,15 @@ module ActionDispatch
@param = (options[:param] || :id).to_sym
@options = options
@shallow = false
+ @api_only = api_only
end
def default_actions
- [:index, :create, :new, :show, :update, :destroy, :edit]
+ if @api_only
+ [:index, :create, :show, :update, :destroy]
+ else
+ [:index, :create, :new, :show, :update, :destroy, :edit]
+ end
end
def actions
@@ -1120,7 +1125,7 @@ module ActionDispatch
end
class SingletonResource < Resource #:nodoc:
- def initialize(entities, options)
+ def initialize(entities, api_only, options)
super
@as = nil
@controller = (options[:controller] || plural).to_s
@@ -1128,7 +1133,11 @@ module ActionDispatch
end
def default_actions
- [:show, :create, :update, :destroy, :new, :edit]
+ if @api_only
+ [:show, :create, :update, :destroy]
+ else
+ [:show, :create, :update, :destroy, :new, :edit]
+ end
end
def plural
@@ -1178,7 +1187,7 @@ module ActionDispatch
return self
end
- resource_scope(:resource, SingletonResource.new(resources.pop, options)) do
+ resource_scope(:resource, SingletonResource.new(resources.pop, api_only?, options)) do
yield if block_given?
concerns(options[:concerns]) if options[:concerns]
@@ -1336,7 +1345,7 @@ module ActionDispatch
return self
end
- resource_scope(:resources, Resource.new(resources.pop, options)) do
+ resource_scope(:resources, Resource.new(resources.pop, api_only?, options)) do
yield if block_given?
concerns(options[:concerns]) if options[:concerns]
@@ -1450,9 +1459,12 @@ module ActionDispatch
parent_resource.instance_of?(Resource) && @scope[:shallow]
end
- # match 'path' => 'controller#action'
- # match 'path', to: 'controller#action'
- # match 'path', 'otherpath', on: :member, via: :get
+ # Matches a url pattern to one or more routes.
+ # For more information, see match[rdoc-ref:Base#match].
+ #
+ # match 'path' => 'controller#action', via: patch
+ # match 'path', to: 'controller#action', via: :post
+ # match 'path', 'otherpath', on: :member, via: :get
def match(path, *rest)
if rest.empty? && Hash === path
options = path
@@ -1765,6 +1777,10 @@ module ActionDispatch
delete :destroy if parent_resource.actions.include?(:destroy)
end
end
+
+ def api_only?
+ @set.api_only?
+ end
end
# Routing Concerns allow you to declare common routes that can be reused
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index d0d8ded515..454593b59f 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -319,17 +319,23 @@ module ActionDispatch
end
def self.new_with_config(config)
+ route_set_config = DEFAULT_CONFIG
+
+ # engines apparently don't have this set
if config.respond_to? :relative_url_root
- new Config.new config.relative_url_root
- else
- # engines apparently don't have this set
- new
+ route_set_config.relative_url_root = config.relative_url_root
+ end
+
+ if config.respond_to? :api_only
+ route_set_config.api_only = config.api_only
end
+
+ new route_set_config
end
- Config = Struct.new :relative_url_root
+ Config = Struct.new :relative_url_root, :api_only
- DEFAULT_CONFIG = Config.new(nil)
+ DEFAULT_CONFIG = Config.new(nil, false)
def initialize(config = DEFAULT_CONFIG)
self.named_routes = NamedRouteCollection.new
@@ -352,6 +358,10 @@ module ActionDispatch
@config.relative_url_root
end
+ def api_only?
+ @config.api_only
+ end
+
def request_class
ActionDispatch::Request
end
@@ -511,10 +521,11 @@ module ActionDispatch
path = conditions.delete :path_info
ast = conditions.delete :parsed_path_info
+ required_defaults = conditions.delete :required_defaults
path = build_path(path, ast, requirements, anchor)
- conditions = build_conditions(conditions, path.names.map(&:to_sym))
+ conditions = build_conditions(conditions)
- route = @set.add_route(app, path, conditions, defaults, name)
+ route = @set.add_route(app, path, conditions, required_defaults, defaults, name)
named_routes[name] = route if name
route
end
@@ -550,7 +561,7 @@ module ActionDispatch
end
private :build_path
- def build_conditions(current_conditions, path_values)
+ def build_conditions(current_conditions)
conditions = current_conditions.dup
# Rack-Mount requires that :request_method be a regular expression.
@@ -563,8 +574,7 @@ module ActionDispatch
end
conditions.keep_if do |k, _|
- k == :action || k == :controller || k == :required_defaults ||
- request_class.public_method_defined?(k) || path_values.include?(k)
+ request_class.public_method_defined?(k)
end
end
private :build_conditions
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index b1bd6ae6d5..dc664d5540 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -294,7 +294,7 @@ module ActionDispatch
if kwarg_request?(args)
process(http_method, path, *args)
else
- non_kwarg_request_warning if args.present?
+ non_kwarg_request_warning if args.any?
process(http_method, path, { params: args[0], headers: args[1] })
end
end
@@ -429,7 +429,6 @@ module ActionDispatch
# reset the html_document variable, except for cookies/assigns calls
unless method == 'cookies' || method == 'assigns'
@html_document = nil
- reset_template_assertion
end
integration_session.__send__(method, *args).tap do
@@ -584,7 +583,7 @@ module ActionDispatch
# https!(false)
# get "/articles/all"
# assert_response :success
- # assert assigns(:articles)
+ # assert_select 'h1', 'Articles'
# end
# end
#
@@ -623,7 +622,7 @@ module ActionDispatch
# def browses_site
# get "/products/all"
# assert_response :success
- # assert assigns(:products)
+ # assert_select 'h1', 'Products'
# end
# end
#
diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb
index 630e6a9b78..415ef80cd2 100644
--- a/actionpack/lib/action_dispatch/testing/test_process.rb
+++ b/actionpack/lib/action_dispatch/testing/test_process.rb
@@ -5,9 +5,9 @@ require 'active_support/core_ext/hash/indifferent_access'
module ActionDispatch
module TestProcess
def assigns(key = nil)
- assigns = {}.with_indifferent_access
- @controller.view_assigns.each { |k, v| assigns.regular_writer(k, v) }
- key.nil? ? assigns : assigns[key]
+ raise NoMethodError,
+ "assigns has been extracted to a gem. To continue using it,
+ add `gem 'rails-controller-testing'` to your Gemfile."
end
def session
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index c1be2c9afe..cc610b6d75 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -232,6 +232,10 @@ class Rack::TestCase < ActionDispatch::IntegrationTest
end
module ActionController
+ class API
+ extend AbstractController::Railties::RoutesHelpers.with(SharedTestRoutes)
+ end
+
class Base
# This stub emulates the Railtie including the URL helpers from a Rails application
extend AbstractController::Railties::RoutesHelpers.with(SharedTestRoutes)
@@ -380,7 +384,7 @@ module RoutingTestHelpers
end
class ResourcesController < ActionController::Base
- def index() render :nothing => true end
+ def index() head :ok end
alias_method :show, :index
end
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index 21586e2193..5b85e83045 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -5,9 +5,6 @@ class ActionPackAssertionsController < ActionController::Base
def nothing() head :ok end
- def hello_world() render :template => "test/hello_world"; end
- def hello_repeating_in_path() render :template => "test/hello/hello"; end
-
def hello_xml_world() render :template => "test/hello_xml_world"; end
def hello_xml_world_pdf
@@ -20,8 +17,6 @@ class ActionPackAssertionsController < ActionController::Base
render :template => "test/hello_xml_world"
end
- def partial() render :partial => 'test/partial'; end
-
def redirect_internal() redirect_to "/nothing"; end
def redirect_to_action() redirect_to :action => "flash_me", :id => 1, :params => { "panda" => "fun" }; end
@@ -73,16 +68,6 @@ class ActionPackAssertionsController < ActionController::Base
render :text => "Hello!", :content_type => Mime::RSS
end
- def render_with_layout
- @variable_for_layout = nil
- render "test/hello_world", :layout => "layouts/standard"
- end
-
- def render_with_layout_and_partial
- @variable_for_layout = nil
- render "test/hello_world_with_partial", :layout => "layouts/standard"
- end
-
def session_stuffing
session['xmas'] = 'turkey'
render :text => "ho ho ho"
@@ -123,7 +108,7 @@ end
module Admin
class InnerModuleController < ActionController::Base
def index
- render :nothing => true
+ head :ok
end
def redirect_to_index
@@ -304,14 +289,6 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
assert session.empty?
end
- def test_render_template_action
- process :nothing
- assert_template nil
-
- process :hello_world
- assert_template 'hello_world'
- end
-
def test_redirection_location
process :redirect_internal
assert_equal 'http://test.host/nothing', @response.redirect_url
@@ -455,192 +432,6 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
end
end
-class AssertTemplateTest < ActionController::TestCase
- tests ActionPackAssertionsController
-
- def test_with_invalid_hash_keys_raises_argument_error
- assert_raise(ArgumentError) do
- assert_template foo: "bar"
- end
- end
-
- def test_with_partial
- get :partial
- assert_template :partial => '_partial'
- end
-
- def test_file_with_absolute_path_success
- get :render_file_absolute_path
- assert_template :file => File.expand_path('../../../README.rdoc', __FILE__)
- end
-
- def test_file_with_relative_path_success
- get :render_file_relative_path
- assert_template :file => 'README.rdoc'
- end
-
- def test_with_file_failure
- get :render_file_absolute_path
- assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_template :file => 'test/hello_world'
- end
-
- get :render_file_absolute_path
- assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_template file: nil
- end
- end
-
- def test_with_nil_passes_when_no_template_rendered
- get :nothing
- assert_template nil
- end
-
- def test_with_nil_fails_when_template_rendered
- get :hello_world
- assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_template nil
- end
- end
-
- def test_with_empty_string_fails_when_template_rendered
- get :hello_world
- assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_template ""
- end
- end
-
- def test_with_empty_string_fails_when_no_template_rendered
- get :nothing
- assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_template ""
- end
- end
-
- def test_passes_with_correct_string
- get :hello_world
- assert_template 'hello_world'
- assert_template 'test/hello_world'
- end
-
- def test_passes_with_correct_symbol
- get :hello_world
- assert_template :hello_world
- end
-
- def test_fails_with_incorrect_string
- get :hello_world
- assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_template 'hello_planet'
- end
- end
-
- def test_fails_with_incorrect_string_that_matches
- get :hello_world
- assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_template 'est/he'
- end
- end
-
- def test_fails_with_repeated_name_in_path
- get :hello_repeating_in_path
- assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_template 'test/hello'
- end
- end
-
- def test_fails_with_incorrect_symbol
- get :hello_world
- assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_template :hello_planet
- end
- end
-
- def test_fails_with_incorrect_symbol_that_matches
- get :hello_world
- assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_template :"est/he"
- end
- end
-
- def test_fails_with_wrong_layout
- get :render_with_layout
- assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_template :layout => "application"
- end
- end
-
- def test_fails_expecting_no_layout
- get :render_with_layout
- assert_raise(ActiveSupport::TestCase::Assertion) do
- assert_template :layout => nil
- end
- end
-
- def test_fails_expecting_not_known_layout
- get :render_with_layout
- assert_raise(ArgumentError) do
- assert_template :layout => 1
- end
- end
-
- def test_passes_with_correct_layout
- get :render_with_layout
- assert_template :layout => "layouts/standard"
- end
-
- def test_passes_with_layout_and_partial
- get :render_with_layout_and_partial
- assert_template :layout => "layouts/standard"
- end
-
- def test_passed_with_no_layout
- get :hello_world
- assert_template :layout => nil
- end
-
- def test_passed_with_no_layout_false
- get :hello_world
- assert_template :layout => false
- end
-
- def test_passes_with_correct_layout_without_layouts_prefix
- get :render_with_layout
- assert_template :layout => "standard"
- end
-
- def test_passes_with_correct_layout_symbol
- get :render_with_layout
- assert_template :layout => :standard
- end
-
- def test_assert_template_reset_between_requests
- get :hello_world
- assert_template 'test/hello_world'
-
- get :nothing
- assert_template nil
-
- get :partial
- assert_template partial: 'test/_partial'
-
- get :nothing
- assert_template partial: nil
-
- get :render_with_layout
- assert_template layout: 'layouts/standard'
-
- get :nothing
- assert_template layout: nil
-
- get :render_file_relative_path
- assert_template file: 'README.rdoc'
-
- get :nothing
- assert_template file: nil
- end
-end
-
class ActionPackHeaderTest < ActionController::TestCase
tests ActionPackAssertionsController
diff --git a/actionpack/test/controller/api/conditional_get_test.rb b/actionpack/test/controller/api/conditional_get_test.rb
new file mode 100644
index 0000000000..d1eb27bf24
--- /dev/null
+++ b/actionpack/test/controller/api/conditional_get_test.rb
@@ -0,0 +1,57 @@
+require 'abstract_unit'
+require 'active_support/core_ext/integer/time'
+require 'active_support/core_ext/numeric/time'
+
+class ConditionalGetApiController < ActionController::API
+ before_action :handle_last_modified_and_etags, only: :two
+
+ def one
+ if stale?(last_modified: Time.now.utc.beginning_of_day, etag: [:foo, 123])
+ render text: "Hi!"
+ end
+ end
+
+ def two
+ render text: "Hi!"
+ end
+
+ private
+
+ def handle_last_modified_and_etags
+ fresh_when(last_modified: Time.now.utc.beginning_of_day, etag: [ :foo, 123 ])
+ end
+end
+
+class ConditionalGetApiTest < ActionController::TestCase
+ tests ConditionalGetApiController
+
+ def setup
+ @last_modified = Time.now.utc.beginning_of_day.httpdate
+ end
+
+ def test_request_gets_last_modified
+ get :two
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ assert_response :success
+ end
+
+ def test_request_obeys_last_modified
+ @request.if_modified_since = @last_modified
+ get :two
+ assert_response :not_modified
+ end
+
+ def test_last_modified_works_with_less_than_too
+ @request.if_modified_since = 5.years.ago.httpdate
+ get :two
+ assert_response :success
+ end
+
+ def test_request_not_modified
+ @request.if_modified_since = @last_modified
+ get :one
+ assert_equal 304, @response.status.to_i
+ assert @response.body.blank?
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ end
+end
diff --git a/actionpack/test/controller/api/data_streaming_test.rb b/actionpack/test/controller/api/data_streaming_test.rb
new file mode 100644
index 0000000000..0e7d97d1f4
--- /dev/null
+++ b/actionpack/test/controller/api/data_streaming_test.rb
@@ -0,0 +1,26 @@
+require 'abstract_unit'
+
+module TestApiFileUtils
+ def file_path() File.expand_path(__FILE__) end
+ def file_data() @data ||= File.open(file_path, 'rb') { |f| f.read } end
+end
+
+class DataStreamingApiController < ActionController::API
+ include TestApiFileUtils
+
+ def one; end
+ def two
+ send_data(file_data, {})
+ end
+end
+
+class DataStreamingApiTest < ActionController::TestCase
+ include TestApiFileUtils
+ tests DataStreamingApiController
+
+ def test_data
+ response = process('two')
+ assert_kind_of String, response.body
+ assert_equal file_data, response.body
+ end
+end
diff --git a/actionpack/test/controller/api/force_ssl_test.rb b/actionpack/test/controller/api/force_ssl_test.rb
new file mode 100644
index 0000000000..8578340d82
--- /dev/null
+++ b/actionpack/test/controller/api/force_ssl_test.rb
@@ -0,0 +1,20 @@
+require 'abstract_unit'
+
+class ForceSSLApiController < ActionController::API
+ force_ssl
+
+ def one; end
+ def two
+ head :ok
+ end
+end
+
+class ForceSSLApiTest < ActionController::TestCase
+ tests ForceSSLApiController
+
+ def test_redirects_to_https
+ get :two
+ assert_response 301
+ assert_equal "https://test.host/force_ssl_api/two", redirect_to_url
+ end
+end
diff --git a/actionpack/test/controller/api/implicit_render_test.rb b/actionpack/test/controller/api/implicit_render_test.rb
new file mode 100644
index 0000000000..26f9cd8f78
--- /dev/null
+++ b/actionpack/test/controller/api/implicit_render_test.rb
@@ -0,0 +1,15 @@
+require 'abstract_unit'
+
+class ImplicitRenderAPITestController < ActionController::API
+ def empty_action
+ end
+end
+
+class ImplicitRenderAPITest < ActionController::TestCase
+ tests ImplicitRenderAPITestController
+
+ def test_implicit_no_content_response
+ get :empty_action
+ assert_response :no_content
+ end
+end
diff --git a/actionpack/test/controller/api/params_wrapper_test.rb b/actionpack/test/controller/api/params_wrapper_test.rb
new file mode 100644
index 0000000000..e40a39d829
--- /dev/null
+++ b/actionpack/test/controller/api/params_wrapper_test.rb
@@ -0,0 +1,26 @@
+require 'abstract_unit'
+
+class ParamsWrapperForApiTest < ActionController::TestCase
+ class UsersController < ActionController::API
+ attr_accessor :last_parameters
+
+ wrap_parameters :person, format: [:json]
+
+ def test
+ self.last_parameters = params.except(:controller, :action)
+ head :ok
+ end
+ end
+
+ class Person; end
+
+ tests UsersController
+
+ def test_specify_wrapper_name
+ @request.env['CONTENT_TYPE'] = 'application/json'
+ post :test, params: { 'username' => 'sikachu' }
+
+ expected = { 'username' => 'sikachu', 'person' => { 'username' => 'sikachu' }}
+ assert_equal expected, @controller.last_parameters
+ end
+end
diff --git a/actionpack/test/controller/api/redirect_to_test.rb b/actionpack/test/controller/api/redirect_to_test.rb
new file mode 100644
index 0000000000..18877c4b3a
--- /dev/null
+++ b/actionpack/test/controller/api/redirect_to_test.rb
@@ -0,0 +1,19 @@
+require 'abstract_unit'
+
+class RedirectToApiController < ActionController::API
+ def one
+ redirect_to action: "two"
+ end
+
+ def two; end
+end
+
+class RedirectToApiTest < ActionController::TestCase
+ tests RedirectToApiController
+
+ def test_redirect_to
+ get :one
+ assert_response :redirect
+ assert_equal "http://test.host/redirect_to_api/two", redirect_to_url
+ end
+end
diff --git a/actionpack/test/controller/api/renderers_test.rb b/actionpack/test/controller/api/renderers_test.rb
new file mode 100644
index 0000000000..9405538833
--- /dev/null
+++ b/actionpack/test/controller/api/renderers_test.rb
@@ -0,0 +1,38 @@
+require 'abstract_unit'
+require 'active_support/core_ext/hash/conversions'
+
+class RenderersApiController < ActionController::API
+ class Model
+ def to_json(options = {})
+ { a: 'b' }.to_json(options)
+ end
+
+ def to_xml(options = {})
+ { a: 'b' }.to_xml(options)
+ end
+ end
+
+ def one
+ render json: Model.new
+ end
+
+ def two
+ render xml: Model.new
+ end
+end
+
+class RenderersApiTest < ActionController::TestCase
+ tests RenderersApiController
+
+ def test_render_json
+ get :one
+ assert_response :success
+ assert_equal({ a: 'b' }.to_json, @response.body)
+ end
+
+ def test_render_xml
+ get :two
+ assert_response :success
+ assert_equal({ a: 'b' }.to_xml, @response.body)
+ end
+end
diff --git a/actionpack/test/controller/api/url_for_test.rb b/actionpack/test/controller/api/url_for_test.rb
new file mode 100644
index 0000000000..0d8691a091
--- /dev/null
+++ b/actionpack/test/controller/api/url_for_test.rb
@@ -0,0 +1,20 @@
+require 'abstract_unit'
+
+class UrlForApiController < ActionController::API
+ def one; end
+ def two; end
+end
+
+class UrlForApiTest < ActionController::TestCase
+ tests UrlForApiController
+
+ def setup
+ super
+ @request.host = 'www.example.com'
+ end
+
+ def test_url_for
+ get :one
+ assert_equal "http://www.example.com/url_for_api/one", @controller.url_for
+ end
+end
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index f7ad8e5158..3240185414 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -13,7 +13,7 @@ end
class NonEmptyController < ActionController::Base
def public_action
- render :nothing => true
+ head :ok
end
end
@@ -29,7 +29,7 @@ end
class OptionalDefaultUrlOptionsController < ActionController::Base
def show
- render nothing: true
+ head :ok
end
def default_url_options
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 2d6607041d..de697858c3 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -352,6 +352,8 @@ class ViewCacheDependencyTest < ActionController::TestCase
end
class CollectionCacheController < ActionController::Base
+ attr_accessor :partial_rendered_times
+
def index
@customers = [Customer.new('david', params[:id] || 1)]
end
@@ -377,14 +379,15 @@ class AutomaticCollectionCacheTest < ActionController::TestCase
super
@controller = CollectionCacheController.new
@controller.perform_caching = true
- @controller.cache_store = ActiveSupport::Cache::MemoryStore.new
+ @controller.partial_rendered_times = 0
end
def test_collection_fetches_cached_views
get :index
+ assert_equal 1, @controller.partial_rendered_times
- ActionView::PartialRenderer.expects(:collection_with_template).never
get :index
+ assert_equal 1, @controller.partial_rendered_times
end
def test_preserves_order_when_reading_from_cache_plus_rendering
@@ -402,8 +405,9 @@ class AutomaticCollectionCacheTest < ActionController::TestCase
def test_caching_works_with_beginning_comment
get :index_with_comment
+ assert_equal 1, @controller.partial_rendered_times
- ActionView::PartialRenderer.expects(:collection_with_template).never
get :index_with_comment
+ assert_equal 1, @controller.partial_rendered_times
end
end
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index a1ce12a13e..9b0487841f 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -13,16 +13,6 @@ class ActionController::Base
filters.map!(&:raw_filter)
end
end
-
- def assigns(key = nil)
- assigns = {}
- instance_variables.each do |ivar|
- next if ActionController::Base.protected_instance_variables.include?(ivar)
- assigns[ivar[1..-1]] = instance_variable_get(ivar)
- end
-
- key.nil? ? assigns : assigns[key.to_s]
- end
end
class FilterTest < ActionController::TestCase
@@ -560,7 +550,7 @@ class FilterTest < ActionController::TestCase
def test_after_actions_are_not_run_if_around_action_does_not_yield
controller = NonYieldingAroundFilterController.new
test_process(controller, "index")
- assert_equal ["filter_one", "it didn't yield"], controller.assigns['filters']
+ assert_equal ["filter_one", "it didn't yield"], controller.instance_variable_get(:@filters)
end
def test_added_action_to_inheritance_graph
@@ -577,140 +567,141 @@ class FilterTest < ActionController::TestCase
def test_running_actions
test_process(PrependingController)
- assert_equal %w( wonderful_life ensure_login ), assigns["ran_filter"]
+ assert_equal %w( wonderful_life ensure_login ),
+ @controller.instance_variable_get(:@ran_filter)
end
def test_running_actions_with_proc
test_process(ProcController)
- assert assigns["ran_proc_action"]
+ assert @controller.instance_variable_get(:@ran_proc_action)
end
def test_running_actions_with_implicit_proc
test_process(ImplicitProcController)
- assert assigns["ran_proc_action"]
+ assert @controller.instance_variable_get(:@ran_proc_action)
end
def test_running_actions_with_class
test_process(AuditController)
- assert assigns["was_audited"]
+ assert @controller.instance_variable_get(:@was_audited)
end
def test_running_anomolous_yet_valid_condition_actions
test_process(AnomolousYetValidConditionController)
- assert_equal %w( ensure_login ), assigns["ran_filter"]
- assert assigns["ran_class_action"]
- assert assigns["ran_proc_action1"]
- assert assigns["ran_proc_action2"]
+ assert_equal %w( ensure_login ), @controller.instance_variable_get(:@ran_filter)
+ assert @controller.instance_variable_get(:@ran_class_action)
+ assert @controller.instance_variable_get(:@ran_proc_action1)
+ assert @controller.instance_variable_get(:@ran_proc_action2)
test_process(AnomolousYetValidConditionController, "show_without_action")
- assert_nil assigns["ran_filter"]
- assert !assigns["ran_class_action"]
- assert !assigns["ran_proc_action1"]
- assert !assigns["ran_proc_action2"]
+ assert_not @controller.instance_variable_defined?(:@ran_filter)
+ assert_not @controller.instance_variable_defined?(:@ran_class_action)
+ assert_not @controller.instance_variable_defined?(:@ran_proc_action1)
+ assert_not @controller.instance_variable_defined?(:@ran_proc_action2)
end
def test_running_conditional_options
test_process(ConditionalOptionsFilter)
- assert_equal %w( ensure_login ), assigns["ran_filter"]
+ assert_equal %w( ensure_login ), @controller.instance_variable_get(:@ran_filter)
end
def test_running_conditional_skip_options
test_process(ConditionalOptionsSkipFilter)
- assert_equal %w( ensure_login ), assigns["ran_filter"]
+ assert_equal %w( ensure_login ), @controller.instance_variable_get(:@ran_filter)
end
def test_if_is_ignored_when_used_with_only
test_process(SkipFilterUsingOnlyAndIf, 'login')
- assert_nil assigns['ran_filter']
+ assert_not @controller.instance_variable_defined?(:@ran_filter)
end
def test_except_is_ignored_when_used_with_if
test_process(SkipFilterUsingIfAndExcept, 'login')
- assert_equal %w(ensure_login), assigns["ran_filter"]
+ assert_equal %w(ensure_login), @controller.instance_variable_get(:@ran_filter)
end
def test_skipping_class_actions
test_process(ClassController)
- assert_equal true, assigns["ran_class_action"]
+ assert_equal true, @controller.instance_variable_get(:@ran_class_action)
skipping_class_controller = Class.new(ClassController) do
skip_before_action ConditionalClassFilter
end
test_process(skipping_class_controller)
- assert_nil assigns['ran_class_action']
+ assert_not @controller.instance_variable_defined?(:@ran_class_action)
end
def test_running_collection_condition_actions
test_process(ConditionalCollectionFilterController)
- assert_equal %w( ensure_login ), assigns["ran_filter"]
+ assert_equal %w( ensure_login ), @controller.instance_variable_get(:@ran_filter)
test_process(ConditionalCollectionFilterController, "show_without_action")
- assert_nil assigns["ran_filter"]
+ assert_not @controller.instance_variable_defined?(:@ran_filter)
test_process(ConditionalCollectionFilterController, "another_action")
- assert_nil assigns["ran_filter"]
+ assert_not @controller.instance_variable_defined?(:@ran_filter)
end
def test_running_only_condition_actions
test_process(OnlyConditionSymController)
- assert_equal %w( ensure_login ), assigns["ran_filter"]
+ assert_equal %w( ensure_login ), @controller.instance_variable_get(:@ran_filter)
test_process(OnlyConditionSymController, "show_without_action")
- assert_nil assigns["ran_filter"]
+ assert_not @controller.instance_variable_defined?(:@ran_filter)
test_process(OnlyConditionProcController)
- assert assigns["ran_proc_action"]
+ assert @controller.instance_variable_get(:@ran_proc_action)
test_process(OnlyConditionProcController, "show_without_action")
- assert !assigns["ran_proc_action"]
+ assert_not @controller.instance_variable_defined?(:@ran_proc_action)
test_process(OnlyConditionClassController)
- assert assigns["ran_class_action"]
+ assert @controller.instance_variable_get(:@ran_class_action)
test_process(OnlyConditionClassController, "show_without_action")
- assert !assigns["ran_class_action"]
+ assert_not @controller.instance_variable_defined?(:@ran_class_action)
end
def test_running_except_condition_actions
test_process(ExceptConditionSymController)
- assert_equal %w( ensure_login ), assigns["ran_filter"]
+ assert_equal %w( ensure_login ), @controller.instance_variable_get(:@ran_filter)
test_process(ExceptConditionSymController, "show_without_action")
- assert_nil assigns["ran_filter"]
+ assert_not @controller.instance_variable_defined?(:@ran_filter)
test_process(ExceptConditionProcController)
- assert assigns["ran_proc_action"]
+ assert @controller.instance_variable_get(:@ran_proc_action)
test_process(ExceptConditionProcController, "show_without_action")
- assert !assigns["ran_proc_action"]
+ assert_not @controller.instance_variable_defined?(:@ran_proc_action)
test_process(ExceptConditionClassController)
- assert assigns["ran_class_action"]
+ assert @controller.instance_variable_get(:@ran_class_action)
test_process(ExceptConditionClassController, "show_without_action")
- assert !assigns["ran_class_action"]
+ assert_not @controller.instance_variable_defined?(:@ran_class_action)
end
def test_running_only_condition_and_conditional_options
test_process(OnlyConditionalOptionsFilter, "show")
- assert_not assigns["ran_conditional_index_proc"]
+ assert_not @controller.instance_variable_defined?(:@ran_conditional_index_proc)
end
def test_running_before_and_after_condition_actions
test_process(BeforeAndAfterConditionController)
- assert_equal %w( ensure_login clean_up_tmp), assigns["ran_filter"]
+ assert_equal %w( ensure_login clean_up_tmp), @controller.instance_variable_get(:@ran_filter)
test_process(BeforeAndAfterConditionController, "show_without_action")
- assert_nil assigns["ran_filter"]
+ assert_not @controller.instance_variable_defined?(:@ran_filter)
end
def test_around_action
test_process(AroundFilterController)
- assert assigns["before_ran"]
- assert assigns["after_ran"]
+ assert @controller.instance_variable_get(:@before_ran)
+ assert @controller.instance_variable_get(:@after_ran)
end
def test_before_after_class_action
test_process(BeforeAfterClassFilterController)
- assert assigns["before_ran"]
- assert assigns["after_ran"]
+ assert @controller.instance_variable_get(:@before_ran)
+ assert @controller.instance_variable_get(:@after_ran)
end
def test_having_properties_in_around_action
test_process(AroundFilterController)
- assert_equal "before and after", assigns["execution_log"]
+ assert_equal "before and after", @controller.instance_variable_get(:@execution_log)
end
def test_prepending_and_appending_around_action
@@ -723,33 +714,33 @@ class FilterTest < ActionController::TestCase
def test_rendering_breaks_actioning_chain
response = test_process(RenderingController)
assert_equal "something else", response.body
- assert !assigns["ran_action"]
+ assert_not @controller.instance_variable_defined?(:@ran_action)
end
def test_before_action_rendering_breaks_actioning_chain_for_after_action
test_process(RenderingController)
- assert_equal %w( before_action_rendering ), assigns["ran_filter"]
- assert !assigns["ran_action"]
+ assert_equal %w( before_action_rendering ), @controller.instance_variable_get(:@ran_filter)
+ assert_not @controller.instance_variable_defined?(:@ran_action)
end
def test_before_action_redirects_breaks_actioning_chain_for_after_action
test_process(BeforeActionRedirectionController)
assert_response :redirect
assert_equal "http://test.host/filter_test/before_action_redirection/target_of_redirection", redirect_to_url
- assert_equal %w( before_action_redirects ), assigns["ran_filter"]
+ assert_equal %w( before_action_redirects ), @controller.instance_variable_get(:@ran_filter)
end
def test_before_action_rendering_breaks_actioning_chain_for_preprend_after_action
test_process(RenderingForPrependAfterActionController)
- assert_equal %w( before_action_rendering ), assigns["ran_filter"]
- assert !assigns["ran_action"]
+ assert_equal %w( before_action_rendering ), @controller.instance_variable_get(:@ran_filter)
+ assert_not @controller.instance_variable_defined?(:@ran_action)
end
def test_before_action_redirects_breaks_actioning_chain_for_preprend_after_action
test_process(BeforeActionRedirectionForPrependAfterActionController)
assert_response :redirect
assert_equal "http://test.host/filter_test/before_action_redirection_for_prepend_after_action/target_of_redirection", redirect_to_url
- assert_equal %w( before_action_redirects ), assigns["ran_filter"]
+ assert_equal %w( before_action_redirects ), @controller.instance_variable_get(:@ran_filter)
end
def test_actions_with_mixed_specialization_run_in_order
@@ -775,26 +766,26 @@ class FilterTest < ActionController::TestCase
def test_running_prepended_before_and_after_action
test_process(PrependingBeforeAndAfterController)
- assert_equal %w( before_all between_before_all_and_after_all after_all ), assigns["ran_filter"]
+ assert_equal %w( before_all between_before_all_and_after_all after_all ), @controller.instance_variable_get(:@ran_filter)
end
def test_skipping_and_limiting_controller
test_process(SkippingAndLimitedController, "index")
- assert_equal %w( ensure_login ), assigns["ran_filter"]
+ assert_equal %w( ensure_login ), @controller.instance_variable_get(:@ran_filter)
test_process(SkippingAndLimitedController, "public")
- assert_nil assigns["ran_filter"]
+ assert_not @controller.instance_variable_defined?(:@ran_filter)
end
def test_skipping_and_reordering_controller
test_process(SkippingAndReorderingController, "index")
- assert_equal %w( find_record ensure_login ), assigns["ran_filter"]
+ assert_equal %w( find_record ensure_login ), @controller.instance_variable_get(:@ran_filter)
end
def test_conditional_skipping_of_actions
test_process(ConditionalSkippingController, "login")
- assert_nil assigns["ran_filter"]
+ assert_not @controller.instance_variable_defined?(:@ran_filter)
test_process(ConditionalSkippingController, "change_password")
- assert_equal %w( ensure_login find_user ), assigns["ran_filter"]
+ assert_equal %w( ensure_login find_user ), @controller.instance_variable_get(:@ran_filter)
test_process(ConditionalSkippingController, "login")
assert !@controller.instance_variable_defined?("@ran_after_action")
@@ -804,23 +795,23 @@ class FilterTest < ActionController::TestCase
def test_conditional_skipping_of_actions_when_parent_action_is_also_conditional
test_process(ChildOfConditionalParentController)
- assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), assigns['ran_filter']
+ assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), @controller.instance_variable_get(:@ran_filter)
test_process(ChildOfConditionalParentController, 'another_action')
- assert_nil assigns['ran_filter']
+ assert_not @controller.instance_variable_defined?(:@ran_filter)
end
def test_condition_skipping_of_actions_when_siblings_also_have_conditions
test_process(ChildOfConditionalParentController)
- assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), assigns['ran_filter']
+ assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), @controller.instance_variable_get(:@ran_filter)
test_process(AnotherChildOfConditionalParentController)
- assert_equal %w( conditional_in_parent_after ), assigns['ran_filter']
+ assert_equal %w( conditional_in_parent_after ), @controller.instance_variable_get(:@ran_filter)
test_process(ChildOfConditionalParentController)
- assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), assigns['ran_filter']
+ assert_equal %w( conditional_in_parent_before conditional_in_parent_after ), @controller.instance_variable_get(:@ran_filter)
end
def test_changing_the_requirements
test_process(ChangingTheRequirementsController, "go_wild")
- assert_nil assigns['ran_filter']
+ assert_not @controller.instance_variable_defined?(:@ran_filter)
end
def test_a_rescuing_around_action
@@ -835,13 +826,13 @@ class FilterTest < ActionController::TestCase
def test_actions_obey_only_and_except_for_implicit_actions
test_process(ImplicitActionsController, 'show')
- assert_equal 'Except', assigns(:except)
- assert_nil assigns(:only)
+ assert_equal 'Except', @controller.instance_variable_get(:@except)
+ assert_not @controller.instance_variable_defined?(:@only)
assert_equal 'show', response.body
test_process(ImplicitActionsController, 'edit')
- assert_equal 'Only', assigns(:only)
- assert_nil assigns(:except)
+ assert_equal 'Only', @controller.instance_variable_get(:@only)
+ assert_not @controller.instance_variable_defined?(:@except)
assert_equal 'edit', response.body
end
@@ -1010,8 +1001,8 @@ class YieldingAroundFiltersTest < ActionController::TestCase
def test_with_proc
test_process(ControllerWithProcFilter,'no_raise')
- assert assigns['before']
- assert assigns['after']
+ assert @controller.instance_variable_get(:@before)
+ assert @controller.instance_variable_get(:@after)
end
def test_nested_actions
@@ -1032,12 +1023,12 @@ class YieldingAroundFiltersTest < ActionController::TestCase
def test_action_order_with_all_action_types
test_process(ControllerWithAllTypesOfFilters,'no_raise')
- assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) after around (after yield)', assigns['ran_filter'].join(' ')
+ assert_equal 'before around (before yield) around_again (before yield) around_again (after yield) after around (after yield)', @controller.instance_variable_get(:@ran_filter).join(' ')
end
def test_action_order_with_skip_action_method
test_process(ControllerWithTwoLessFilters,'no_raise')
- assert_equal 'before around (before yield) around (after yield)', assigns['ran_filter'].join(' ')
+ assert_equal 'before around (before yield) around (after yield)', @controller.instance_variable_get(:@ran_filter).join(' ')
end
def test_first_action_in_multiple_before_action_chain_halts
@@ -1063,7 +1054,7 @@ class YieldingAroundFiltersTest < ActionController::TestCase
def test_skipping_with_skip_action_callback
test_process(SkipFilterUsingSkipActionCallback,'no_raise')
- assert_equal 'before around (before yield) around (after yield)', assigns['ran_filter'].join(' ')
+ assert_equal 'before around (before yield) around (after yield)', @controller.instance_variable_get(:@ran_filter).join(' ')
end
def test_deprecated_skip_action_callback
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index 0ff0a1ef61..64543f0659 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -57,7 +57,7 @@ class FlashTest < ActionController::TestCase
def std_action
@flash_copy = {}.update(flash)
- render :nothing => true
+ head :ok
end
def filter_halting_action
@@ -103,54 +103,55 @@ class FlashTest < ActionController::TestCase
get :set_flash
get :use_flash
- assert_equal "hello", assigns["flash_copy"]["that"]
- assert_equal "hello", assigns["flashy"]
+ assert_equal "hello", @controller.instance_variable_get(:@flash_copy)["that"]
+ assert_equal "hello", @controller.instance_variable_get(:@flashy)
get :use_flash
- assert_nil assigns["flash_copy"]["that"], "On second flash"
+ assert_nil @controller.instance_variable_get(:@flash_copy)["that"], "On second flash"
end
def test_keep_flash
get :set_flash
get :use_flash_and_keep_it
- assert_equal "hello", assigns["flash_copy"]["that"]
- assert_equal "hello", assigns["flashy"]
+ assert_equal "hello", @controller.instance_variable_get(:@flash_copy)["that"]
+ assert_equal "hello", @controller.instance_variable_get(:@flashy)
get :use_flash
- assert_equal "hello", assigns["flash_copy"]["that"], "On second flash"
+ assert_equal "hello", @controller.instance_variable_get(:@flash_copy)["that"], "On second flash"
get :use_flash
- assert_nil assigns["flash_copy"]["that"], "On third flash"
+ assert_nil @controller.instance_variable_get(:@flash_copy)["that"], "On third flash"
end
def test_flash_now
get :set_flash_now
- assert_equal "hello", assigns["flash_copy"]["that"]
- assert_equal "bar" , assigns["flash_copy"]["foo"]
- assert_equal "hello", assigns["flashy"]
+ assert_equal "hello", @controller.instance_variable_get(:@flash_copy)["that"]
+ assert_equal "bar", @controller.instance_variable_get(:@flash_copy)["foo"]
+ assert_equal "hello", @controller.instance_variable_get(:@flashy)
get :attempt_to_use_flash_now
- assert_nil assigns["flash_copy"]["that"]
- assert_nil assigns["flash_copy"]["foo"]
- assert_nil assigns["flashy"]
+ assert_nil @controller.instance_variable_get(:@flash_copy)["that"]
+ assert_nil @controller.instance_variable_get(:@flash_copy)["foo"]
+ assert_nil @controller.instance_variable_get(:@flashy)
end
def test_update_flash
get :set_flash
get :use_flash_and_update_it
- assert_equal "hello", assigns["flash_copy"]["that"]
- assert_equal "hello again", assigns["flash_copy"]["this"]
+ assert_equal "hello", @controller.instance_variable_get(:@flash_copy)["that"]
+ assert_equal "hello again", @controller.instance_variable_get(:@flash_copy)["this"]
get :use_flash
- assert_nil assigns["flash_copy"]["that"], "On second flash"
- assert_equal "hello again", assigns["flash_copy"]["this"], "On second flash"
+ assert_nil @controller.instance_variable_get(:@flash_copy)["that"], "On second flash"
+ assert_equal "hello again",
+ @controller.instance_variable_get(:@flash_copy)["this"], "On second flash"
end
def test_flash_after_reset_session
get :use_flash_after_reset_session
- assert_equal "hello", assigns["flashy_that"]
- assert_equal "good-bye", assigns["flashy_this"]
- assert_nil assigns["flashy_that_reset"]
+ assert_equal "hello", @controller.instance_variable_get(:@flashy_that)
+ assert_equal "good-bye", @controller.instance_variable_get(:@flashy_this)
+ assert_nil @controller.instance_variable_get(:@flashy_that_reset)
end
def test_does_not_set_the_session_if_the_flash_is_empty
@@ -160,13 +161,13 @@ class FlashTest < ActionController::TestCase
def test_sweep_after_halted_action_chain
get :std_action
- assert_nil assigns["flash_copy"]["foo"]
+ assert_nil @controller.instance_variable_get(:@flash_copy)["foo"]
get :filter_halting_action
- assert_equal "bar", assigns["flash_copy"]["foo"]
+ assert_equal "bar", @controller.instance_variable_get(:@flash_copy)["foo"]
get :std_action # follow redirection
- assert_equal "bar", assigns["flash_copy"]["foo"]
+ assert_equal "bar", @controller.instance_variable_get(:@flash_copy)["foo"]
get :std_action
- assert_nil assigns["flash_copy"]["foo"]
+ assert_nil @controller.instance_variable_get(:@flash_copy)["foo"]
end
def test_keep_and_discard_return_values
diff --git a/actionpack/test/controller/force_ssl_test.rb b/actionpack/test/controller/force_ssl_test.rb
index 5639abdc56..72ae30eb39 100644
--- a/actionpack/test/controller/force_ssl_test.rb
+++ b/actionpack/test/controller/force_ssl_test.rb
@@ -240,8 +240,8 @@ class ForceSSLFlashTest < ActionController::TestCase
@request.env.delete('PATH_INFO')
get :use_flash
- assert_equal "hello", assigns["flash_copy"]["that"]
- assert_equal "hello", assigns["flashy"]
+ assert_equal "hello", @controller.instance_variable_get("@flash_copy")["that"]
+ assert_equal "hello", @controller.instance_variable_get("@flashy")
end
end
diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb
index 10fbee7582..adaf19c2dc 100644
--- a/actionpack/test/controller/http_basic_authentication_test.rb
+++ b/actionpack/test/controller/http_basic_authentication_test.rb
@@ -13,7 +13,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
end
def display
- render :text => 'Definitely Maybe'
+ render :text => 'Definitely Maybe' if @logged_in
end
def show
@@ -122,7 +122,6 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
get :display
assert_response :success
- assert assigns(:logged_in)
assert_equal 'Definitely Maybe', @response.body
end
diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb
index 52a0bc9aa3..57964769a7 100644
--- a/actionpack/test/controller/http_digest_authentication_test.rb
+++ b/actionpack/test/controller/http_digest_authentication_test.rb
@@ -14,7 +14,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
end
def display
- render :text => 'Definitely Maybe'
+ render :text => 'Definitely Maybe' if @logged_in
end
private
@@ -124,7 +124,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
get :display
assert_response :success
- assert assigns(:logged_in)
assert_equal 'Definitely Maybe', @response.body
end
@@ -134,7 +133,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
get :display
assert_response :success
- assert assigns(:logged_in)
assert_equal 'Definitely Maybe', @response.body
end
@@ -144,7 +142,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
get :display
assert_response :success
- assert assigns(:logged_in)
assert_equal 'Definitely Maybe', @response.body
end
@@ -156,7 +153,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
get :display
assert_response :success
- assert assigns(:logged_in)
assert_equal 'Definitely Maybe', @response.body
end
@@ -167,7 +163,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
get :display
assert_response :success
- assert assigns(:logged_in)
assert_equal 'Definitely Maybe', @response.body
end
@@ -180,7 +175,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
get :display
assert_response :success
- assert assigns(:logged_in)
assert_equal 'Definitely Maybe', @response.body
end
@@ -191,7 +185,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
get :display
assert_response :success
- assert assigns(:logged_in)
assert_equal 'Definitely Maybe', @response.body
end
@@ -201,7 +194,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
put :display
assert_response :success
- assert assigns(:logged_in)
assert_equal 'Definitely Maybe', @response.body
end
@@ -244,7 +236,6 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
get :display
assert_response :success
- assert assigns(:logged_in)
assert_equal 'Definitely Maybe', @response.body
end
diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb
index 2521f0322c..802c17b6bf 100644
--- a/actionpack/test/controller/http_token_authentication_test.rb
+++ b/actionpack/test/controller/http_token_authentication_test.rb
@@ -80,13 +80,20 @@ class HttpTokenAuthenticationTest < ActionController::TestCase
end
test "authentication request with badly formatted header" do
- @request.env['HTTP_AUTHORIZATION'] = "Token foobar"
+ @request.env['HTTP_AUTHORIZATION'] = 'Token token$"lifo"'
get :index
assert_response :unauthorized
assert_equal "HTTP Token: Access denied.\n", @response.body, "Authentication header was not properly parsed"
end
+ test "successful authentication request with Bearer instead of Token" do
+ @request.env['HTTP_AUTHORIZATION'] = 'Bearer lifo'
+ get :index
+
+ assert_response :success
+ end
+
test "authentication request without credential" do
get :display
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index a87059bee4..a6460168cb 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -315,8 +315,6 @@ class IntegrationTestTest < ActiveSupport::TestCase
session1 = @test.open_session { |sess| }
session2 = @test.open_session # implicit session
- assert_respond_to session1, :assert_template, "open_session makes assert_template available"
- assert_respond_to session2, :assert_template, "open_session makes assert_template available"
assert !session1.equal?(session2)
end
diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb
index 03a4ad7823..ccbf336acf 100644
--- a/actionpack/test/controller/log_subscriber_test.rb
+++ b/actionpack/test/controller/log_subscriber_test.rb
@@ -19,7 +19,7 @@ module Another
end
def show
- render :nothing => true
+ head :ok
end
def redirector
@@ -170,7 +170,7 @@ class ACLogSubscriberTest < ActionController::TestCase
def test_process_action_with_view_runtime
get :show
wait
- assert_match(/\(Views: [\d.]+ms\)/, logs[1])
+ assert_match(/Completed 200 OK in [\d]ms/, logs[1])
end
def test_append_info_to_payload_is_called_even_with_exception
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index ef30f1ea0f..4f5ca46b04 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -50,11 +50,6 @@ class RedirectController < ActionController::Base
redirect_to :controller => 'module_test/module_redirect', :action => "hello_world"
end
- def redirect_with_assigns
- @hello = "world"
- redirect_to :action => "hello_world"
- end
-
def redirect_to_url
redirect_to "http://www.rubyonrails.org/"
end
@@ -215,12 +210,6 @@ class RedirectTest < ActionController::TestCase
assert_redirected_to :controller => 'module_test/module_redirect', :action => 'hello_world'
end
- def test_redirect_with_assigns
- get :redirect_with_assigns
- assert_response :redirect
- assert_equal "world", assigns["hello"]
- end
-
def test_redirect_to_url
get :redirect_to_url
assert_response :redirect
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 79e2104789..c9c43de37d 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -24,6 +24,11 @@ class TestControllerWithExtraEtags < ActionController::Base
end
end
+class ImplicitRenderTestController < ActionController::Base
+ def empty_action
+ end
+end
+
class TestController < ActionController::Base
protect_from_forgery
@@ -120,6 +125,10 @@ class TestController < ActionController::Base
render :action => 'hello_world'
end
+ def respond_with_empty_body
+ render nothing: true
+ end
+
def conditional_hello_with_bangs
render :action => 'hello_world'
end
@@ -270,6 +279,12 @@ class ExpiresInRenderTest < ActionController::TestCase
assert_match(/no-transform/, @response.headers["Cache-Control"])
end
+ def test_render_nothing_deprecated
+ assert_deprecated do
+ get :respond_with_empty_body
+ end
+ end
+
def test_date_header_when_expires_in
time = Time.mktime(2011,10,30)
Time.stubs(:now).returns(time)
@@ -453,6 +468,15 @@ class MetalRenderTest < ActionController::TestCase
end
end
+class ImplicitRenderTest < ActionController::TestCase
+ tests ImplicitRenderTestController
+
+ def test_implicit_no_content_response
+ get :empty_action
+ assert_response :no_content
+ end
+end
+
class HeadRenderTest < ActionController::TestCase
tests TestController
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index f8cf79a257..82c808754c 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -72,17 +72,17 @@ class RequestForgeryProtectionControllerUsingNullSession < ActionController::Bas
def signed
cookies.signed[:foo] = 'bar'
- render :nothing => true
+ head :ok
end
def encrypted
cookies.encrypted[:foo] = 'bar'
- render :nothing => true
+ head :ok
end
def try_to_reset_session
reset_session
- render :nothing => true
+ head :ok
end
end
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index e348749f78..86ffb898ad 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -48,6 +48,14 @@ class TestCaseTest < ActionController::TestCase
render text: params.inspect
end
+ def test_query_parameters
+ render text: request.query_parameters.inspect
+ end
+
+ def test_request_parameters
+ render text: request.request_parameters.inspect
+ end
+
def test_uri
render text: request.fullpath
end
@@ -129,13 +137,7 @@ XML
def delete_cookie
cookies.delete("foo")
- render nothing: true
- end
-
- def test_assigns
- @foo = "foo"
- @foo_hash = { foo: :bar }
- render nothing: true
+ head :ok
end
def test_without_body
@@ -166,17 +168,6 @@ XML
end
end
- class ViewAssignsController < ActionController::Base
- def test_assigns
- @foo = "foo"
- render nothing: true
- end
-
- def view_assigns
- { "bar" => "bar" }
- end
- end
-
class DefaultUrlOptionsCachingController < ActionController::Base
before_action { @dynamic_opt = 'opt' }
@@ -438,30 +429,6 @@ XML
assert_equal "OK", @response.body
end
- def test_assigns
- process :test_assigns
- # assigns can be accessed using assigns(key)
- # or assigns[key], where key is a string or
- # a symbol
- assert_equal "foo", assigns(:foo)
- assert_equal "foo", assigns("foo")
- assert_equal "foo", assigns[:foo]
- assert_equal "foo", assigns["foo"]
-
- # but the assigned variable should not have its own keys stringified
- expected_hash = { foo: :bar }
- assert_equal expected_hash, assigns(:foo_hash)
- end
-
- def test_view_assigns
- @controller = ViewAssignsController.new
- process :test_assigns
- assert_equal nil, assigns(:foo)
- assert_equal nil, assigns[:foo]
- assert_equal "bar", assigns(:bar)
- assert_equal "bar", assigns[:bar]
- end
-
def test_should_not_impose_childless_html_tags_in_xml
process :test_xml_output
@@ -547,6 +514,18 @@ XML
)
end
+ def test_query_param_named_action
+ get :test_query_parameters, params: {action: 'foobar'}
+ parsed_params = eval(@response.body)
+ assert_equal({action: 'foobar'}, parsed_params)
+ end
+
+ def test_request_param_named_action
+ post :test_request_parameters, params: {action: 'foobar'}
+ parsed_params = eval(@response.body)
+ assert_equal({'action' => 'foobar'}, parsed_params)
+ end
+
def test_kwarg_params_passing_with_session_and_flash
get :test_params, params: {
page: {
@@ -908,6 +887,11 @@ XML
assert_equal File.open(path, READ_PLAIN).read, plain_file_upload.read
end
+ def test_fixture_file_upload_should_be_able_access_to_tempfile
+ file = fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg")
+ assert file.respond_to?(:tempfile), "expected tempfile should respond on fixture file object, got nothing"
+ end
+
def test_fixture_file_upload
post :test_file_upload,
params: {
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index f208cfda89..27ee8603e4 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -663,6 +663,7 @@ class RequestMethod < BaseRequestTest
assert_equal 'GET', request.request_method
assert_equal 'GET', request.env["REQUEST_METHOD"]
+ assert request.get?
end
test "invalid http method raises exception" do
diff --git a/actionpack/test/dispatch/routing/route_set_test.rb b/actionpack/test/dispatch/routing/route_set_test.rb
index fe52c50336..9327fe12c6 100644
--- a/actionpack/test/dispatch/routing/route_set_test.rb
+++ b/actionpack/test/dispatch/routing/route_set_test.rb
@@ -17,6 +17,16 @@ module ActionDispatch
@set = RouteSet.new
end
+ test "not being empty when route is added" do
+ assert empty?
+
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ end
+
+ assert_not empty?
+ end
+
test "url helpers are added when route is added" do
draw do
get 'foo', to: SimpleApp.new('foo#index')
@@ -136,6 +146,10 @@ module ActionDispatch
def url_helpers
@set.url_helpers
end
+
+ def empty?
+ @set.empty?
+ end
end
end
end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 62c99a2edc..77f86b7a62 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -167,6 +167,46 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/session/reset', reset_session_path
end
+ def test_session_singleton_resource_for_api_app
+ self.class.stub_controllers do |_|
+ config = ActionDispatch::Routing::RouteSet::Config.new
+ config.api_only = true
+
+ routes = ActionDispatch::Routing::RouteSet.new(config)
+
+ routes.draw do
+ resource :session do
+ get :create
+ post :reset
+ end
+ end
+ @app = RoutedRackApp.new routes
+ end
+
+ get '/session'
+ assert_equal 'sessions#create', @response.body
+ assert_equal '/session', session_path
+
+ post '/session'
+ assert_equal 'sessions#create', @response.body
+
+ put '/session'
+ assert_equal 'sessions#update', @response.body
+
+ delete '/session'
+ assert_equal 'sessions#destroy', @response.body
+
+ post '/session/reset'
+ assert_equal 'sessions#reset', @response.body
+ assert_equal '/session/reset', reset_session_path
+
+ get '/session/new'
+ assert_equal 'Not Found', @response.body
+
+ get '/session/edit'
+ assert_equal 'Not Found', @response.body
+ end
+
def test_session_info_nested_singleton_resource
draw do
resource :session do
@@ -509,6 +549,41 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/projects/1/edit', edit_project_path(:id => '1')
end
+ def test_projects_for_api_app
+ self.class.stub_controllers do |_|
+ config = ActionDispatch::Routing::RouteSet::Config.new
+ config.api_only = true
+
+ routes = ActionDispatch::Routing::RouteSet.new(config)
+ routes.draw do
+ resources :projects, controller: :project
+ end
+ @app = RoutedRackApp.new routes
+ end
+
+ get '/projects'
+ assert_equal 'project#index', @response.body
+ assert_equal '/projects', projects_path
+
+ post '/projects'
+ assert_equal 'project#create', @response.body
+
+ get '/projects.xml'
+ assert_equal 'project#index', @response.body
+ assert_equal '/projects.xml', projects_path(format: 'xml')
+
+ get '/projects/1'
+ assert_equal 'project#show', @response.body
+ assert_equal '/projects/1', project_path(id: '1')
+
+ get '/projects/1.xml'
+ assert_equal 'project#show', @response.body
+ assert_equal '/projects/1.xml', project_path(id: '1', format: 'xml')
+
+ get '/projects/1/edit'
+ assert_equal 'Not Found', @response.body
+ end
+
def test_projects_with_post_action_and_new_path_on_collection
draw do
resources :projects, :controller => :project do
diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb
index 72eaa916bc..cbb12a2209 100644
--- a/actionpack/test/dispatch/show_exceptions_test.rb
+++ b/actionpack/test/dispatch/show_exceptions_test.rb
@@ -11,7 +11,7 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest
when "/bad_params"
raise ActionDispatch::ParamsParser::ParseError.new("", StandardError.new)
when "/method_not_allowed"
- raise ActionController::MethodNotAllowed
+ raise ActionController::MethodNotAllowed, 'PUT'
when "/unknown_http_method"
raise ActionController::UnknownHttpMethod
when "/not_found_original_exception"
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index 93e5c85a97..95971b3a0e 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -57,6 +57,7 @@ module StaticTests
def test_serves_static_index_file_in_directory
assert_html "/foo/index.html", get("/foo/index.html")
+ assert_html "/foo/index.html", get("/foo/index")
assert_html "/foo/index.html", get("/foo/")
assert_html "/foo/index.html", get("/foo")
end
@@ -260,6 +261,19 @@ class StaticTest < ActiveSupport::TestCase
}
assert_equal(DummyApp.call(nil), @app.call(env))
end
+
+ def test_non_default_static_index
+ @app = ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60", index: "other-index")
+ assert_html "/other-index.html", get("/other-index.html")
+ assert_html "/other-index.html", get("/other-index")
+ assert_html "/other-index.html", get("/")
+ assert_html "/other-index.html", get("")
+ assert_html "/foo/other-index.html", get("/foo/other-index.html")
+ assert_html "/foo/other-index.html", get("/foo/other-index")
+ assert_html "/foo/other-index.html", get("/foo/")
+ assert_html "/foo/other-index.html", get("/foo")
+ end
+
end
class StaticEncodingTest < StaticTest
diff --git a/actionpack/test/dispatch/template_assertions_test.rb b/actionpack/test/dispatch/template_assertions_test.rb
deleted file mode 100644
index 7278754b49..0000000000
--- a/actionpack/test/dispatch/template_assertions_test.rb
+++ /dev/null
@@ -1,110 +0,0 @@
-require 'abstract_unit'
-
-class AssertTemplateController < ActionController::Base
- def render_with_partial
- render partial: 'test/partial'
- end
-
- def render_with_template
- render 'test/hello_world'
- end
-
- def render_with_layout
- @variable_for_layout = 'hello'
- render 'test/hello_world', layout: "layouts/standard"
- end
-
- def render_with_file
- render file: 'README.rdoc'
- end
-
- def render_nothing
- head :ok
- end
-end
-
-class AssertTemplateControllerTest < ActionDispatch::IntegrationTest
- def test_template_reset_between_requests
- get '/assert_template/render_with_template'
- assert_template 'test/hello_world'
-
- get '/assert_template/render_nothing'
- assert_template nil
- end
-
- def test_partial_reset_between_requests
- get '/assert_template/render_with_partial'
- assert_template partial: 'test/_partial'
-
- get '/assert_template/render_nothing'
- assert_template partial: nil
- end
-
- def test_layout_reset_between_requests
- get '/assert_template/render_with_layout'
- assert_template layout: 'layouts/standard'
-
- get '/assert_template/render_nothing'
- assert_template layout: nil
- end
-
- def test_file_reset_between_requests
- get '/assert_template/render_with_file'
- assert_template file: 'README.rdoc'
-
- get '/assert_template/render_nothing'
- assert_template file: nil
- end
-
- def test_template_reset_between_requests_when_opening_a_session
- open_session do |session|
- session.get '/assert_template/render_with_template'
- session.assert_template 'test/hello_world'
-
- session.get '/assert_template/render_nothing'
- session.assert_template nil
- end
- end
-
- def test_partial_reset_between_requests_when_opening_a_session
- open_session do |session|
- session.get '/assert_template/render_with_partial'
- session.assert_template partial: 'test/_partial'
-
- session.get '/assert_template/render_nothing'
- session.assert_template partial: nil
- end
- end
-
- def test_layout_reset_between_requests_when_opening_a_session
- open_session do |session|
- session.get '/assert_template/render_with_layout'
- session.assert_template layout: 'layouts/standard'
-
- session.get '/assert_template/render_nothing'
- session.assert_template layout: nil
- end
- end
-
- def test_file_reset_between_requests_when_opening_a_session
- open_session do |session|
- session.get '/assert_template/render_with_file'
- session.assert_template file: 'README.rdoc'
-
- session.get '/assert_template/render_nothing'
- session.assert_template file: nil
- end
- end
-
- def test_assigns_do_not_reset_template_assertion
- get '/assert_template/render_with_layout'
- assert_equal 'hello', assigns(:variable_for_layout)
- assert_template layout: 'layouts/standard'
- end
-
- def test_cookies_do_not_reset_template_assertion
- get '/assert_template/render_with_layout'
- cookies
- assert_template layout: 'layouts/standard'
- end
-end
diff --git a/actionpack/test/fixtures/customers/_commented_customer.html.erb b/actionpack/test/fixtures/customers/_commented_customer.html.erb
index d5f6e3b491..8cc9c1ec13 100644
--- a/actionpack/test/fixtures/customers/_commented_customer.html.erb
+++ b/actionpack/test/fixtures/customers/_commented_customer.html.erb
@@ -1,4 +1,5 @@
<%# I'm a comment %>
<% cache customer do %>
+ <% controller.partial_rendered_times += 1 %>
<%= customer.name %>, <%= customer.id %>
<% end %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/customers/_customer.html.erb b/actionpack/test/fixtures/customers/_customer.html.erb
index 67e9f6d411..5105090d4b 100644
--- a/actionpack/test/fixtures/customers/_customer.html.erb
+++ b/actionpack/test/fixtures/customers/_customer.html.erb
@@ -1,3 +1,4 @@
<% cache customer do %>
+ <% controller.partial_rendered_times += 1 %>
<%= customer.name %>, <%= customer.id %>
<% end %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layouts/standard.html.erb b/actionpack/test/fixtures/layouts/standard.html.erb
index 5e6c24fe39..48882dca35 100644
--- a/actionpack/test/fixtures/layouts/standard.html.erb
+++ b/actionpack/test/fixtures/layouts/standard.html.erb
@@ -1 +1 @@
-<html><%= yield %><%= @variable_for_layout %></html> \ No newline at end of file
+<html><%= yield %><%= @variable_for_layout %></html>
diff --git a/actionpack/test/fixtures/public/foo/other-index.html b/actionpack/test/fixtures/public/foo/other-index.html
new file mode 100644
index 0000000000..51c90c26ea
--- /dev/null
+++ b/actionpack/test/fixtures/public/foo/other-index.html
@@ -0,0 +1 @@
+/foo/other-index.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/public/other-index.html b/actionpack/test/fixtures/public/other-index.html
new file mode 100644
index 0000000000..0820dfcb6e
--- /dev/null
+++ b/actionpack/test/fixtures/public/other-index.html
@@ -0,0 +1 @@
+/other-index.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/公共/foo/other-index.html b/actionpack/test/fixtures/公共/foo/other-index.html
new file mode 100644
index 0000000000..51c90c26ea
--- /dev/null
+++ b/actionpack/test/fixtures/公共/foo/other-index.html
@@ -0,0 +1 @@
+/foo/other-index.html \ No newline at end of file
diff --git a/actionpack/test/fixtures/公共/other-index.html b/actionpack/test/fixtures/公共/other-index.html
new file mode 100644
index 0000000000..0820dfcb6e
--- /dev/null
+++ b/actionpack/test/fixtures/公共/other-index.html
@@ -0,0 +1 @@
+/other-index.html \ No newline at end of file
diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb
index 0980df06e8..eff96a0abc 100644
--- a/actionpack/test/journey/route_test.rb
+++ b/actionpack/test/journey/route_test.rb
@@ -7,7 +7,7 @@ module ActionDispatch
app = Object.new
path = Path::Pattern.from_string '/:controller(/:action(/:id(.:format)))'
defaults = {}
- route = Route.new("name", app, path, {}, defaults)
+ route = Route.new("name", app, path, {}, [], defaults)
assert_equal app, route.app
assert_equal path, route.path
@@ -18,7 +18,7 @@ module ActionDispatch
app = Object.new
path = Path::Pattern.from_string '/:controller(/:action(/:id(.:format)))'
defaults = {}
- route = Route.new("name", app, path, {}, defaults)
+ route = Route.new("name", app, path, {}, [], defaults)
route.ast.grep(Nodes::Terminal).each do |node|
assert_equal route, node.memo
@@ -29,27 +29,27 @@ module ActionDispatch
strexp = Router::Strexp.build(':name', { name: /love/ }, ['/'])
path = Path::Pattern.new strexp
defaults = { name: 'tender' }
- route = Route.new('name', nil, path, nil, defaults)
+ route = Route.new('name', nil, path, nil, [], defaults)
assert_equal(/love/, route.requirements[:name])
end
def test_ip_address
path = Path::Pattern.from_string '/messages/:id(.:format)'
- route = Route.new("name", nil, path, {:ip => '192.168.1.1'},
+ route = Route.new("name", nil, path, {:ip => '192.168.1.1'}, [],
{ :controller => 'foo', :action => 'bar' })
assert_equal '192.168.1.1', route.ip
end
def test_default_ip
path = Path::Pattern.from_string '/messages/:id(.:format)'
- route = Route.new("name", nil, path, {},
+ route = Route.new("name", nil, path, {}, [],
{ :controller => 'foo', :action => 'bar' })
assert_equal(//, route.ip)
end
def test_format_with_star
path = Path::Pattern.from_string '/:controller/*extra'
- route = Route.new("name", nil, path, {},
+ route = Route.new("name", nil, path, {}, [],
{ :controller => 'foo', :action => 'bar' })
assert_equal '/foo/himom', route.format({
:controller => 'foo',
@@ -59,7 +59,7 @@ module ActionDispatch
def test_connects_all_match
path = Path::Pattern.from_string '/:controller(/:action(/:id(.:format)))'
- route = Route.new("name", nil, path, {:action => 'bar'}, { :controller => 'foo' })
+ route = Route.new("name", nil, path, {:action => 'bar'}, [], { :controller => 'foo' })
assert_equal '/foo/bar/10', route.format({
:controller => 'foo',
@@ -70,34 +70,34 @@ module ActionDispatch
def test_extras_are_not_included_if_optional
path = Path::Pattern.from_string '/page/:id(/:action)'
- route = Route.new("name", nil, path, { }, { :action => 'show' })
+ route = Route.new("name", nil, path, { }, [], { :action => 'show' })
assert_equal '/page/10', route.format({ :id => 10 })
end
def test_extras_are_not_included_if_optional_with_parameter
path = Path::Pattern.from_string '(/sections/:section)/pages/:id'
- route = Route.new("name", nil, path, { }, { :action => 'show' })
+ route = Route.new("name", nil, path, { }, [], { :action => 'show' })
assert_equal '/pages/10', route.format({:id => 10})
end
def test_extras_are_not_included_if_optional_parameter_is_nil
path = Path::Pattern.from_string '(/sections/:section)/pages/:id'
- route = Route.new("name", nil, path, { }, { :action => 'show' })
+ route = Route.new("name", nil, path, { }, [], { :action => 'show' })
assert_equal '/pages/10', route.format({:id => 10, :section => nil})
end
def test_score
- constraints = {:required_defaults => [:controller, :action]}
+ constraints = {}
defaults = {:controller=>"pages", :action=>"show"}
path = Path::Pattern.from_string "/page/:id(/:action)(.:format)"
- specific = Route.new "name", nil, path, constraints, defaults
+ specific = Route.new "name", nil, path, constraints, [:controller, :action], defaults
path = Path::Pattern.from_string "/:controller(/:action(/:id))(.:format)"
- generic = Route.new "name", nil, path, constraints
+ 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 a134e343cc..802fb93c69 100644
--- a/actionpack/test/journey/router_test.rb
+++ b/actionpack/test/journey/router_test.rb
@@ -35,7 +35,7 @@ module ActionDispatch
exp = Router::Strexp.build '/foo-bar-baz', {}, ['/.?']
path = Path::Pattern.new exp
- routes.add_route nil, path, {}, {:id => nil}, {}
+ routes.add_route nil, path, {}, [], {:id => nil}, {}
env = rails_env 'PATH_INFO' => '/foo-bar-baz'
called = false
@@ -52,7 +52,7 @@ module ActionDispatch
exp = Router::Strexp.build '/%E3%81%BB%E3%81%92', {}, ['/.?']
path = Path::Pattern.new exp
- routes.add_route nil, path, {}, {:id => nil}, {}
+ routes.add_route nil, path, {}, [], {:id => nil}, {}
env = rails_env 'PATH_INFO' => '/%E3%81%BB%E3%81%92'
called = false
@@ -71,7 +71,7 @@ module ActionDispatch
exp = Router::Strexp.build '/foo(/:id)', {}, ['/.?']
path = Path::Pattern.new exp
- routes.add_route nil, path, requirements, {:id => nil}, {}
+ routes.add_route nil, path, requirements, [], {:id => nil}, {}
env = rails_env({'PATH_INFO' => '/foo/10'}, klass)
router.recognize(env) do |r, params|
@@ -91,7 +91,7 @@ module ActionDispatch
exp = Router::Strexp.build '/foo(/:id)', {}, ['/.?']
path = Path::Pattern.new exp
- router.routes.add_route nil, path, requirements, {:id => nil}, {}
+ router.routes.add_route nil, path, requirements, [], {:id => nil}, {}
env = rails_env({'PATH_INFO' => '/foo/10'}, klass)
router.recognize(env) do |r, params|
@@ -118,7 +118,7 @@ module ActionDispatch
exp = Router::Strexp.build '/bar', {}, ['/.?']
path = Path::Pattern.new exp
- routes.add_route nil, path, {}, {}, {}
+ routes.add_route nil, path, {}, [], {}, {}
env = rails_env({'PATH_INFO' => '/foo',
'custom.path_info' => '/bar'}, CustomPathRequest)
@@ -192,7 +192,7 @@ module ActionDispatch
route_name = "gorby_thunderhorse"
pattern = Router::Strexp.build("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false)
path = Path::Pattern.new pattern
- @router.routes.add_route nil, path, {}, {}, route_name
+ @router.routes.add_route nil, path, {}, [], {}, route_name
error = assert_raises(ActionController::UrlGenerationError) do
@formatter.generate(route_name, { }, { })
@@ -234,7 +234,7 @@ module ActionDispatch
def test_defaults_merge_correctly
path = Path::Pattern.from_string '/foo(/:id)'
- @router.routes.add_route nil, path, {}, {:id => nil}, {}
+ @router.routes.add_route nil, path, {}, [], {:id => nil}, {}
env = rails_env 'PATH_INFO' => '/foo/10'
@router.recognize(env) do |r, params|
@@ -317,7 +317,7 @@ module ActionDispatch
def test_nil_path_parts_are_ignored
path = Path::Pattern.from_string "/:controller(/:action(.:format))"
- @router.routes.add_route @app, path, {}, {}, {}
+ @router.routes.add_route @app, path, {}, [], {}, {}
params = { :controller => "tasks", :format => nil }
extras = { :action => 'lol' }
@@ -332,7 +332,7 @@ module ActionDispatch
str = Router::Strexp.build("/", Hash[params], ['/', '.', '?'], true)
path = Path::Pattern.new str
- @router.routes.add_route @app, path, {}, {}, {}
+ @router.routes.add_route @app, path, {}, [], {}, {}
path, _ = @formatter.generate(nil, Hash[params], {})
assert_equal '/', path
@@ -340,7 +340,7 @@ module ActionDispatch
def test_generate_calls_param_proc
path = Path::Pattern.from_string '/:controller(/:action)'
- @router.routes.add_route @app, path, {}, {}, {}
+ @router.routes.add_route @app, path, {}, [], {}, {}
parameterized = []
params = [ [:controller, "tasks"],
@@ -357,7 +357,7 @@ module ActionDispatch
def test_generate_id
path = Path::Pattern.from_string '/:controller(/:action)'
- @router.routes.add_route @app, path, {}, {}, {}
+ @router.routes.add_route @app, path, {}, [], {}, {}
path, params = @formatter.generate(
nil, {:id=>1, :controller=>"tasks", :action=>"show"}, {})
@@ -367,7 +367,7 @@ module ActionDispatch
def test_generate_escapes
path = Path::Pattern.from_string '/:controller(/:action)'
- @router.routes.add_route @app, path, {}, {}, {}
+ @router.routes.add_route @app, path, {}, [], {}, {}
path, _ = @formatter.generate(nil,
{ :controller => "tasks",
@@ -378,7 +378,7 @@ module ActionDispatch
def test_generate_escapes_with_namespaced_controller
path = Path::Pattern.from_string '/:controller(/:action)'
- @router.routes.add_route @app, path, {}, {}, {}
+ @router.routes.add_route @app, path, {}, [], {}, {}
path, _ = @formatter.generate(
nil, { :controller => "admin/tasks",
@@ -389,7 +389,7 @@ module ActionDispatch
def test_generate_extra_params
path = Path::Pattern.from_string '/:controller(/:action)'
- @router.routes.add_route @app, path, {}, {}, {}
+ @router.routes.add_route @app, path, {}, [], {}, {}
path, params = @formatter.generate(
nil, { :id => 1,
@@ -403,7 +403,7 @@ module ActionDispatch
def test_generate_missing_keys_no_matches_different_format_keys
path = Path::Pattern.from_string '/:controller/:action/:name'
- @router.routes.add_route @app, path, {}, {}, {}
+ @router.routes.add_route @app, path, {}, [], {}, {}
primarty_parameters = {
:id => 1,
:controller => "tasks",
@@ -430,7 +430,7 @@ module ActionDispatch
def test_generate_uses_recall_if_needed
path = Path::Pattern.from_string '/:controller(/:action(/:id))'
- @router.routes.add_route @app, path, {}, {}, {}
+ @router.routes.add_route @app, path, {}, [], {}, {}
path, params = @formatter.generate(
nil,
@@ -442,7 +442,7 @@ module ActionDispatch
def test_generate_with_name
path = Path::Pattern.from_string '/:controller(/:action)'
- @router.routes.add_route @app, path, {}, {}, "tasks"
+ @router.routes.add_route @app, path, {}, [], {}, "tasks"
path, params = @formatter.generate(
"tasks",
@@ -460,7 +460,7 @@ module ActionDispatch
define_method("test_recognize_#{expected.keys.map(&:to_s).join('_')}") do
path = Path::Pattern.from_string "/:controller(/:action(/:id))"
app = Object.new
- route = @router.routes.add_route(app, path, {}, {}, {})
+ route = @router.routes.add_route(app, path, {}, [], {}, {})
env = rails_env 'PATH_INFO' => request_path
called = false
@@ -482,7 +482,7 @@ module ActionDispatch
define_method("test_recognize_#{name}") do
path = Path::Pattern.from_string '/:segment/*splat'
app = Object.new
- route = @router.routes.add_route(app, path, {}, {}, {})
+ route = @router.routes.add_route(app, path, {}, [], {}, {})
env = rails_env 'PATH_INFO' => request_path
called = false
@@ -505,7 +505,7 @@ module ActionDispatch
)
path = Path::Pattern.new strexp
app = Object.new
- route = @router.routes.add_route(app, path, {}, {}, {})
+ route = @router.routes.add_route(app, path, {}, [], {}, {})
env = rails_env 'PATH_INFO' => '/admin/users/show/10'
called = false
@@ -526,7 +526,7 @@ module ActionDispatch
def test_recognize_literal
path = Path::Pattern.from_string "/books(/:action(.:format))"
app = Object.new
- route = @router.routes.add_route(app, path, {}, {:controller => 'books'})
+ route = @router.routes.add_route(app, path, {}, [], {:controller => 'books'})
env = rails_env 'PATH_INFO' => '/books/list.rss'
expected = { :controller => 'books', :action => 'list', :format => 'rss' }
@@ -544,7 +544,7 @@ module ActionDispatch
path = Path::Pattern.from_string "/books(/:action(.:format))"
app = Object.new
conditions = { request_method: 'HEAD' }
- @router.routes.add_route(app, path, conditions, {})
+ @router.routes.add_route(app, path, conditions, [], {})
env = rails_env(
'PATH_INFO' => '/books/list.rss',
@@ -565,7 +565,7 @@ module ActionDispatch
conditions = {
:request_method => 'GET'
}
- @router.routes.add_route(app, path, conditions, {})
+ @router.routes.add_route(app, path, conditions, [], {})
env = rails_env 'PATH_INFO' => '/books/list.rss',
"REQUEST_METHOD" => "HEAD"
@@ -582,7 +582,7 @@ module ActionDispatch
path = Path::Pattern.from_string "/books(/:action(.:format))"
app = Object.new
conditions = { request_method: 'GET' }
- @router.routes.add_route(app, path, conditions, {})
+ @router.routes.add_route(app, path, conditions, [], {})
env = rails_env 'PATH_INFO' => '/books/list.rss',
"REQUEST_METHOD" => "POST"
@@ -597,7 +597,7 @@ module ActionDispatch
conditions = conditions.dup
conditions[:request_method] = 'POST'
- post = @router.routes.add_route(app, path, conditions, {})
+ post = @router.routes.add_route(app, path, conditions, [], {})
called = false
@router.recognize(env) do |r, params|
@@ -617,7 +617,7 @@ module ActionDispatch
else
path = Path::Pattern.new path
end
- router.routes.add_route @app, path, {}, {}, {}
+ router.routes.add_route @app, path, {}, [], {}, {}
end
end
diff --git a/actionpack/test/journey/routes_test.rb b/actionpack/test/journey/routes_test.rb
index b54d961f66..b9dac8751c 100644
--- a/actionpack/test/journey/routes_test.rb
+++ b/actionpack/test/journey/routes_test.rb
@@ -13,10 +13,12 @@ module ActionDispatch
path = Path::Pattern.new exp
requirements = { :hello => /world/ }
- routes.add_route nil, path, requirements, {:id => nil}, {}
+ routes.add_route nil, path, requirements, [], {:id => nil}, {}
+ assert_not routes.empty?
assert_equal 1, routes.length
routes.clear
+ assert routes.empty?
assert_equal 0, routes.length
end
@@ -24,9 +26,9 @@ module ActionDispatch
routes = Routes.new
path = Path::Pattern.from_string '/hello'
- routes.add_route nil, path, {}, {}, {}
+ routes.add_route nil, path, {}, [], {}, {}
ast = routes.ast
- routes.add_route nil, path, {}, {}, {}
+ routes.add_route nil, path, {}, [], {}, {}
assert_not_equal ast, routes.ast
end
@@ -34,16 +36,16 @@ module ActionDispatch
routes = Routes.new
path = Path::Pattern.from_string '/hello'
- routes.add_route nil, path, {}, {}, {}
+ routes.add_route nil, path, {}, [], {}, {}
sim = routes.simulator
- routes.add_route nil, path, {}, {}, {}
+ routes.add_route nil, path, {}, [], {}, {}
assert_not_equal sim, routes.simulator
end
def test_partition_route
path = Path::Pattern.from_string '/hello'
- anchored_route = @routes.add_route nil, path, {}, {}, {}
+ anchored_route = @routes.add_route nil, path, {}, [], {}, {}
assert_equal [anchored_route], @routes.anchored_routes
assert_equal [], @routes.custom_routes
@@ -52,7 +54,7 @@ module ActionDispatch
)
path = Path::Pattern.new strexp
- custom_route = @routes.add_route nil, path, {}, {}, {}
+ custom_route = @routes.add_route nil, path, {}, [], {}, {}
assert_equal [custom_route], @routes.custom_routes
assert_equal [anchored_route], @routes.anchored_routes
end
@@ -63,8 +65,8 @@ module ActionDispatch
one = Path::Pattern.from_string '/hello'
two = Path::Pattern.from_string '/aaron'
- routes.add_route nil, one, {}, {}, 'aaron'
- routes.add_route nil, two, {}, {}, 'aaron'
+ routes.add_route nil, one, {}, [], {}, 'aaron'
+ routes.add_route nil, two, {}, [], {}, 'aaron'
assert_equal '/hello', routes.named_routes['aaron'].path.spec.to_s
end
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index a6d5c08fe9..7b6008d5ed 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,13 @@
+* Raise an ArgumentError when a false value for `include_blank` is passed to a
+ required select field (to comply with the HTML5 spec).
+
+ *Grey Baker*
+
+* Do not put partial name to `local_assigns` when rendering without
+ an object or a collection.
+
+ *Henrik Nygren*
+
* Remove `:rescue_format` option for `translate` helper since it's no longer
supported by I18n.
@@ -120,7 +130,7 @@
*Angelo Capilleri*
-* Allow entries without a link tag in AtomFeedHelper.
+* Allow entries without a link tag in `AtomFeedHelper`.
*Daniel Gomez de Souza*
diff --git a/actionview/README.rdoc b/actionview/README.rdoc
index 5bb62c7562..8b1f85f748 100644
--- a/actionview/README.rdoc
+++ b/actionview/README.rdoc
@@ -9,7 +9,7 @@ used to inline short Ruby snippets inside HTML), and XML Builder.
The latest version of Action View can be installed with RubyGems:
- % [sudo] gem install actionview
+ % gem install actionview
Source code can be downloaded as part of the Rails project on GitHub
diff --git a/actionview/Rakefile b/actionview/Rakefile
index 2b752b83df..93be50721d 100644
--- a/actionview/Rakefile
+++ b/actionview/Rakefile
@@ -1,5 +1,4 @@
require 'rake/testtask'
-require 'rubygems/package_task'
desc "Default Task"
task :default => :test
@@ -45,39 +44,8 @@ namespace :test do
end
end
-spec = eval(File.read('actionview.gemspec'))
-
-Gem::PackageTask.new(spec) do |p|
- p.gem_spec = spec
-end
-
-desc "Release to rubygems"
-task :release => :package do
- require 'rake/gemcutter'
- Rake::Gemcutter::Tasks.new(spec).define
- Rake::Task['gem:push'].invoke
-end
-
task :lines do
- lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
-
- FileList["lib/**/*.rb"].each do |file_name|
- next if file_name =~ /vendor/
- File.open(file_name, 'r') do |f|
- while line = f.gets
- lines += 1
- next if line =~ /^\s*$/
- next if line =~ /^\s*#/
- codelines += 1
- end
- end
- puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
-
- total_lines += lines
- total_codelines += codelines
-
- lines, codelines = 0, 0
- end
-
- puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
+ load File.expand_path('..', File.dirname(__FILE__)) + '/tools/line_statistics'
+ files = FileList["lib/**/*.rb"]
+ CodeTools::LineStatistics.new(files).print_loc
end
diff --git a/actionview/lib/action_view/helpers/atom_feed_helper.rb b/actionview/lib/action_view/helpers/atom_feed_helper.rb
index d8be4e5678..bb1cdd0f8d 100644
--- a/actionview/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionview/lib/action_view/helpers/atom_feed_helper.rb
@@ -16,7 +16,7 @@ module ActionView
# end
#
# app/controllers/posts_controller.rb:
- # class PostsController < ApplicationController::Base
+ # class PostsController < ApplicationController
# # GET /posts.html
# # GET /posts.atom
# def index
@@ -51,7 +51,7 @@ module ActionView
# * <tt>:language</tt>: Defaults to "en-US".
# * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
# * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
- # * <tt>:id</tt>: The id for this feed. Defaults to "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}"
+ # * <tt>:id</tt>: The id for this feed. Defaults to "tag:localhost,2005:/posts", in this case.
# * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
# created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
# 2005 is used (as an "I don't care" value).
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 4fe21ca05b..72e2aa1807 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -39,7 +39,7 @@ module ActionView
# This will include both records as part of the cache key and updating either of them will
# expire the cache.
#
- # ==== Template digest
+ # ==== \Template digest
#
# The template digest that's added to the cache key is computed by taking an md5 of the
# contents of the entire template file. This ensures that your caches will automatically
@@ -75,7 +75,8 @@ module ActionView
# render(topics) => render("topics/topic")
# render(message.topics) => render("topics/topic")
#
- # It's not possible to derive all render calls like that, though. Here are a few examples of things that can't be derived:
+ # It's not possible to derive all render calls like that, though.
+ # Here are a few examples of things that can't be derived:
#
# render group_of_attachments
# render @project.documents.where(published: true).order('created_at')
@@ -97,19 +98,21 @@ module ActionView
# <%# Template Dependency: todolists/todolist %>
# <%= render_sortable_todolists @project.todolists %>
#
- # The pattern used to match these is /# Template Dependency: ([^ ]+)/, so it's important that you type it out just so.
+ # The pattern used to match these is <tt>/# Template Dependency: (\S+)/</tt>,
+ # so it's important that you type it out just so.
# You can only declare one template dependency per line.
#
# === External dependencies
#
- # If you use a helper method, for example, inside of a cached block and you then update that helper,
- # you'll have to bump the cache as well. It doesn't really matter how you do it, but the md5 of the template file
+ # If you use a helper method, for example, inside a cached block and
+ # you then update that helper, you'll have to bump the cache as well.
+ # It doesn't really matter how you do it, but the md5 of the template file
# must change. One recommendation is to simply be explicit in a comment, like:
#
# <%# Helper Dependency Updated: May 6, 2012 at 6pm %>
# <%= some_helper_method(person) %>
#
- # Now all you'll have to do is change that timestamp when the helper method changes.
+ # Now all you have to do is change that timestamp when the helper method changes.
#
# === Automatic Collection Caching
#
@@ -118,7 +121,7 @@ module ActionView
# <%= render @notifications %>
# <%= render partial: 'notifications/notification', collection: @notifications %>
#
- # If the notifications/_notification partial starts with a cache call like so:
+ # If the notifications/_notification partial starts with a cache call as:
#
# <% cache notification do %>
# <%= notification.name %>
@@ -127,8 +130,9 @@ module ActionView
# The collection can then automatically use any cached renders for that
# template by reading them at once instead of one by one.
#
- # See ActionView::Template::Handlers::ERB.resource_cache_call_pattern for more
- # information on what cache calls make a template eligible for this collection caching.
+ # See ActionView::Template::Handlers::ERB.resource_cache_call_pattern for
+ # more information on what cache calls make a template eligible for this
+ # collection caching.
#
# The automatic cache multi read can be turned off like so:
#
diff --git a/actionview/lib/action_view/helpers/date_helper.rb b/actionview/lib/action_view/helpers/date_helper.rb
index 46ce7cf0be..394e20ff2b 100644
--- a/actionview/lib/action_view/helpers/date_helper.rb
+++ b/actionview/lib/action_view/helpers/date_helper.rb
@@ -488,7 +488,7 @@ module ActionView
# selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
# Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
#
- # my_time = Time.now + 6.hours
+ # my_time = Time.now + 10.minutes
#
# # Generates a select field for minutes that defaults to the minutes for the time in my_time.
# select_minute(my_time)
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 06394df3d4..3a9acafaa2 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -1670,6 +1670,7 @@ module ActionView
# label(:terms) do
# 'Accept <a href="/terms">Terms</a>.'.html_safe
# end
+ # # => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label>
def label(method, text = nil, options = {}, &block)
@template.label(@object_name, method, text, objectify_options(options), &block)
end
diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb
index 38fee3b314..d3deee0df3 100644
--- a/actionview/lib/action_view/helpers/form_options_helper.rb
+++ b/actionview/lib/action_view/helpers/form_options_helper.rb
@@ -410,7 +410,7 @@ module ActionView
# * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
# * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
# array of child objects representing the <tt><option></tt> tags.
- # * group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
+ # * +group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
# string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
# * +option_key_method+ - The name of a method which, when called on a child object of a member of
# +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb
index ca8d30e4ef..13effa592d 100644
--- a/actionview/lib/action_view/helpers/number_helper.rb
+++ b/actionview/lib/action_view/helpers/number_helper.rb
@@ -279,7 +279,7 @@ module ActionView
# See <tt>number_to_human_size</tt> if you want to print a file
# size.
#
- # You can also define you own unit-quantifier names if you want
+ # You can also define your own unit-quantifier names if you want
# to use other decimal units (eg.: 1500 becomes "1.5
# kilometers", 0.150 becomes "150 milliliters", etc). You may
# define a wide range of unit quantifiers, even fractional ones
diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb
index acc6443a96..d57f26ba4f 100644
--- a/actionview/lib/action_view/helpers/tags/base.rb
+++ b/actionview/lib/action_view/helpers/tags/base.rb
@@ -120,7 +120,12 @@ module ActionView
def select_content_tag(option_tags, options, html_options)
html_options = html_options.stringify_keys
add_default_name_and_id(html_options)
- options[:include_blank] ||= true unless options[:prompt] || select_not_required?(html_options)
+
+ if placeholder_required?(html_options)
+ raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
+ options[:include_blank] ||= true unless options[:prompt]
+ end
+
value = options.fetch(:selected) { value(object) }
select = content_tag("select", add_options(option_tags, options, value), html_options)
@@ -131,8 +136,9 @@ module ActionView
end
end
- def select_not_required?(html_options)
- !html_options["required"] || html_options["multiple"] || html_options["size"].to_i > 1
+ def placeholder_required?(html_options)
+ # See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
+ html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
end
def add_options(option_tags, options, value = nil)
diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb
index 1fc609f2cd..9d636c8c9e 100644
--- a/actionview/lib/action_view/layouts.rb
+++ b/actionview/lib/action_view/layouts.rb
@@ -315,25 +315,16 @@ module ActionView
name_clause
end
- if self._layout_conditions.empty?
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def _layout
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def _layout
+ if _conditional_layout?
#{layout_definition}
+ else
+ #{name_clause}
end
- private :_layout
- RUBY
- else
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def _layout
- if _conditional_layout?
- #{layout_definition}
- else
- #{name_clause}
- end
- end
- private :_layout
- RUBY
- end
+ end
+ private :_layout
+ RUBY
end
private
diff --git a/actionview/lib/action_view/railtie.rb b/actionview/lib/action_view/railtie.rb
index 9a26cba574..5dc7950d6b 100644
--- a/actionview/lib/action_view/railtie.rb
+++ b/actionview/lib/action_view/railtie.rb
@@ -48,8 +48,10 @@ module ActionView
end
end
- rake_tasks do
- load "action_view/tasks/dependencies.rake"
+ rake_tasks do |app|
+ unless app.config.api_only
+ load "action_view/tasks/dependencies.rake"
+ end
end
end
end
diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb
index cd151c0189..b751bca31e 100644
--- a/actionview/lib/action_view/renderer/partial_renderer.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer.rb
@@ -338,7 +338,7 @@ module ActionView
end
object ||= locals[as]
- locals[as] = object
+ locals[as] = object if @has_object
content = @template.render(view, locals) do |*name|
view._layout_for(*name, &block)
diff --git a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
index b77c884e66..c8268e226e 100644
--- a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
@@ -6,7 +6,7 @@ module ActionView
included do
# Fallback cache store if Action View is used without Rails.
- # Otherwise overriden in Railtie to use Rails.cache.
+ # Otherwise overridden in Railtie to use Rails.cache.
mattr_accessor(:collection_cache) { ActiveSupport::Cache::MemoryStore.new }
end
diff --git a/actionview/test/actionpack/controller/layout_test.rb b/actionview/test/actionpack/controller/layout_test.rb
index 64ab125637..64bc4c41d6 100644
--- a/actionview/test/actionpack/controller/layout_test.rb
+++ b/actionview/test/actionpack/controller/layout_test.rb
@@ -122,14 +122,6 @@ class PrependsViewPathController < LayoutTest
end
end
-class ParentController < LayoutTest
- layout 'item'
-end
-
-class ChildController < ParentController
- layout 'layout_test', only: :hello
-end
-
class OnlyLayoutController < LayoutTest
layout 'item', :only => "hello"
end
@@ -157,75 +149,75 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_layout_set_when_using_default_layout
@controller = DefaultLayoutController.new
get :hello
- assert_template :layout => "layouts/layout_test"
+ assert_includes @response.body, 'layout_test.erb'
end
def test_layout_set_when_using_streaming_layout
@controller = StreamingLayoutController.new
get :hello
- assert_template :hello
+ assert_includes @response.body, 'layout_test.erb'
end
def test_layout_set_when_set_in_controller
@controller = HasOwnLayoutController.new
get :hello
- assert_template :layout => "layouts/item"
+ assert_includes @response.body, 'item.erb'
end
def test_layout_symbol_set_in_controller_returning_nil_falls_back_to_default
@controller = HasNilLayoutSymbol.new
get :hello
- assert_template layout: "layouts/layout_test"
+ assert_includes @response.body, 'layout_test.erb'
end
def test_layout_proc_set_in_controller_returning_nil_falls_back_to_default
@controller = HasNilLayoutProc.new
get :hello
- assert_template layout: "layouts/layout_test"
+ assert_includes @response.body, 'layout_test.erb'
end
def test_layout_only_exception_when_included
@controller = OnlyLayoutController.new
get :hello
- assert_template :layout => "layouts/item"
+ assert_includes @response.body, 'item.erb'
end
def test_layout_only_exception_when_excepted
@controller = OnlyLayoutController.new
get :goodbye
- assert !@response.body.include?("item.erb"), "#{@response.body.inspect} included 'item.erb'"
+ assert_not_includes @response.body, 'item.erb'
end
def test_layout_except_exception_when_included
@controller = ExceptLayoutController.new
get :hello
- assert_template :layout => "layouts/item"
+ assert_includes @response.body, 'item.erb'
end
def test_layout_except_exception_when_excepted
@controller = ExceptLayoutController.new
get :goodbye
- assert !@response.body.include?("item.erb"), "#{@response.body.inspect} included 'item.erb'"
+ assert_not_includes @response.body, 'item.erb'
end
def test_layout_set_when_using_render
with_template_handler :mab, lambda { |template| template.source.inspect } do
@controller = SetsLayoutInRenderController.new
get :hello
- assert_template :layout => "layouts/third_party_template_library"
+ assert_includes @response.body, 'layouts/third_party_template_library.mab'
end
end
def test_layout_is_not_set_when_none_rendered
@controller = RendersNoLayoutController.new
get :hello
- assert_template :layout => nil
+ assert_equal 'hello.erb', @response.body
end
def test_layout_is_picked_from_the_controller_instances_view_path
@controller = PrependsViewPathController.new
get :hello
- assert_template :layout => /layouts\/alt/
+ assert_includes @response.body, 'alt.erb'
end
def test_absolute_pathed_layout
@@ -233,12 +225,6 @@ class LayoutSetInResponseTest < ActionController::TestCase
get :hello
assert_equal "layout_test.erb hello.erb", @response.body.strip
end
-
- def test_respect_to_parent_layout
- @controller = ChildController.new
- get :goodbye
- assert_template :layout => "layouts/item"
- end
end
class SetsNonExistentLayoutFile < LayoutTest
@@ -276,7 +262,7 @@ unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
@controller = LayoutSymlinkedTest.new
get :hello
assert_response 200
- assert_template :layout => "layouts/symlinked/symlinked_layout"
+ assert_includes @response.body, 'This is my layout'
end
end
end
diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb
index 8b47536a18..c4228b5683 100644
--- a/actionview/test/actionpack/controller/render_test.rb
+++ b/actionview/test/actionpack/controller/render_test.rb
@@ -1,5 +1,6 @@
require 'abstract_unit'
-require "active_model"
+require 'active_model'
+require 'fileutils'
class ApplicationController < ActionController::Base
self.view_paths = File.join(FIXTURE_LOAD_PATH, "actionpack")
@@ -357,7 +358,7 @@ class TestController < ApplicationController
end
def rendering_nothing_on_layout
- render :nothing => true
+ head :ok
end
def render_to_string_with_assigns
@@ -678,25 +679,32 @@ class RenderTest < ActionController::TestCase
ActionView::Base.logger = nil
end
+ def case_sensitive_file_system?
+ fname = '.case_sensitive_file_system_test'
+ FileUtils.touch(fname)
+ !File.exist?(fname.upcase)
+ ensure
+ FileUtils.rm_f(fname)
+ end
+
# :ported:
def test_simple_show
get :hello_world
assert_response 200
assert_response :success
- assert_template "test/hello_world"
assert_equal "<html>Hello world!</html>", @response.body
end
# :ported:
def test_renders_default_template_for_missing_action
get :'hyphen-ated'
- assert_template 'test/hyphen-ated'
+ assert_equal "hyphen-ated.erb", @response.body
end
# :ported:
def test_render
get :render_hello_world
- assert_template "test/hello_world"
+ assert_equal "Hello world!", @response.body
end
def test_line_offset
@@ -712,20 +720,18 @@ class RenderTest < ActionController::TestCase
# :ported: compatibility
def test_render_with_forward_slash
get :render_hello_world_with_forward_slash
- assert_template "test/hello_world"
+ assert_equal "Hello world!", @response.body
end
# :ported:
def test_render_in_top_directory
get :render_template_in_top_directory
- assert_template "shared"
assert_equal "Elastica", @response.body
end
# :ported:
def test_render_in_top_directory_with_slash
get :render_template_in_top_directory_with_slash
- assert_template "shared"
assert_equal "Elastica", @response.body
end
@@ -743,12 +749,19 @@ class RenderTest < ActionController::TestCase
# :ported:
def test_render_action
get :render_action_hello_world
- assert_template "test/hello_world"
+ assert_equal "Hello world!", @response.body
end
def test_render_action_upcased
- assert_raise ActionView::MissingTemplate do
- get :render_action_upcased_hello_world
+ action = :render_action_upcased_hello_world
+
+ if case_sensitive_file_system?
+ assert_raise ActionView::MissingTemplate do
+ get action
+ end
+ else
+ get action
+ assert_equal 'Hello world!', @response.body
end
end
@@ -756,13 +769,12 @@ class RenderTest < ActionController::TestCase
def test_render_action_hello_world_as_string
get :render_action_hello_world_as_string
assert_equal "Hello world!", @response.body
- assert_template "test/hello_world"
end
# :ported:
def test_render_action_with_symbol
get :render_action_hello_world_with_symbol
- assert_template "test/hello_world"
+ assert_equal "Hello world!", @response.body
end
# :ported:
@@ -950,7 +962,7 @@ class RenderTest < ActionController::TestCase
def test_render_to_string_inline
get :render_to_string_with_inline_and_render
- assert_template "test/hello_world"
+ assert_equal 'Hello world!', @response.body
end
# :ported:
@@ -1035,8 +1047,8 @@ class RenderTest < ActionController::TestCase
def test_render_to_string_doesnt_break_assigns
get :render_to_string_with_assigns
- assert_equal "i'm before the render", assigns(:before)
- assert_equal "i'm after the render", assigns(:after)
+ assert_equal "i'm before the render", @controller.instance_variable_get(:@before)
+ assert_equal "i'm after the render", @controller.instance_variable_get(:@after)
end
def test_bad_render_to_string_still_throws_exception
@@ -1045,8 +1057,8 @@ class RenderTest < ActionController::TestCase
def test_render_to_string_that_throws_caught_exception_doesnt_break_assigns
assert_nothing_raised { get :render_to_string_with_caught_exception }
- assert_equal "i'm before the render", assigns(:before)
- assert_equal "i'm after the render", assigns(:after)
+ assert_equal "i'm before the render", @controller.instance_variable_get(:@before)
+ assert_equal "i'm after the render", @controller.instance_variable_get(:@after)
end
def test_accessing_params_in_template_with_layout
@@ -1107,7 +1119,7 @@ class RenderTest < ActionController::TestCase
# :addressed:
def test_render_text_with_assigns
get :render_text_with_assigns
- assert_equal "world", assigns["hello"]
+ assert_equal "world", @controller.instance_variable_get(:@hello)
end
def test_render_text_with_assigns_option
@@ -1173,22 +1185,22 @@ class RenderTest < ActionController::TestCase
def test_render_to_string_partial
get :render_to_string_with_partial
- assert_equal "only partial", assigns(:partial_only)
- assert_equal "Hello: david", assigns(:partial_with_locals)
+ assert_equal "only partial", @controller.instance_variable_get(:@partial_only)
+ assert_equal "Hello: david", @controller.instance_variable_get(:@partial_with_locals)
assert_equal "text/html", @response.content_type
end
def test_render_to_string_with_template_and_html_partial
get :render_to_string_with_template_and_html_partial
- assert_equal "**only partial**\n", assigns(:text)
- assert_equal "<strong>only partial</strong>\n", assigns(:html)
+ assert_equal "**only partial**\n", @controller.instance_variable_get(:@text)
+ assert_equal "<strong>only partial</strong>\n", @controller.instance_variable_get(:@html)
assert_equal "<strong>only html partial</strong>\n", @response.body
assert_equal "text/html", @response.content_type
end
def test_render_to_string_and_render_with_different_formats
get :render_to_string_and_render_with_different_formats
- assert_equal "<strong>only partial</strong>\n", assigns(:html)
+ assert_equal "<strong>only partial</strong>\n", @controller.instance_variable_get(:@html)
assert_equal "**only partial**\n", @response.body
assert_equal "text/plain", @response.content_type
end
@@ -1212,21 +1224,18 @@ class RenderTest < ActionController::TestCase
def test_partial_with_form_builder
get :partial_with_form_builder
- assert_match(/<label/, @response.body)
- assert_template('test/_form')
+ assert_equal "<label for=\"post_title\">Title</label>\n", @response.body
end
def test_partial_with_form_builder_subclass
get :partial_with_form_builder_subclass
- assert_match(/<label/, @response.body)
- assert_template('test/_labelling_form')
+ assert_equal "<label for=\"post_title\">Title</label>\n", @response.body
end
def test_nested_partial_with_form_builder
@controller = Fun::GamesController.new
get :nested_partial_with_form_builder
- assert_match(/<label/, @response.body)
- assert_template('fun/games/_form')
+ assert_equal "<label for=\"post_title\">Title</label>\n", @response.body
end
def test_namespaced_object_partial
@@ -1270,48 +1279,29 @@ class RenderTest < ActionController::TestCase
assert_equal "Bonjour: davidBonjour: mary", @response.body
end
- def test_locals_option_to_assert_template_is_not_supported
- get :partial_collection_with_locals
-
- warning_buffer = StringIO.new
- $stderr = warning_buffer
-
- assert_template partial: 'customer_greeting', locals: { greeting: 'Bonjour' }
- assert_equal "the :locals option to #assert_template is only supported in a ActionView::TestCase\n", warning_buffer.string
- ensure
- $stderr = STDERR
- end
-
def test_partial_collection_with_spacer
get :partial_collection_with_spacer
assert_equal "Hello: davidonly partialHello: mary", @response.body
- assert_template :partial => '_customer'
end
def test_partial_collection_with_spacer_which_uses_render
get :partial_collection_with_spacer_which_uses_render
assert_equal "Hello: davidpartial html\npartial with partial\nHello: mary", @response.body
- assert_template :partial => '_customer'
end
def test_partial_collection_shorthand_with_locals
get :partial_collection_shorthand_with_locals
assert_equal "Bonjour: davidBonjour: mary", @response.body
- assert_template :partial => 'customers/_customer', :count => 2
- assert_template :partial => '_completely_fake_and_made_up_template_that_cannot_possibly_be_rendered', :count => 0
end
def test_partial_collection_shorthand_with_different_types_of_records
get :partial_collection_shorthand_with_different_types_of_records
assert_equal "Bonjour bad customer: mark0Bonjour good customer: craig1Bonjour bad customer: john2Bonjour good customer: zach3Bonjour good customer: brandon4Bonjour bad customer: dan5", @response.body
- assert_template :partial => 'good_customers/_good_customer', :count => 3
- assert_template :partial => 'bad_customers/_bad_customer', :count => 3
end
def test_empty_partial_collection
get :empty_partial_collection
assert_equal " ", @response.body
- assert_template :partial => false
end
def test_partial_with_hash_object
diff --git a/actionview/test/activerecord/render_partial_with_record_identification_test.rb b/actionview/test/activerecord/render_partial_with_record_identification_test.rb
index 409370104d..9772ebb39e 100644
--- a/actionview/test/activerecord/render_partial_with_record_identification_test.rb
+++ b/actionview/test/activerecord/render_partial_with_record_identification_test.rb
@@ -52,43 +52,37 @@ class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase
def test_rendering_partial_with_has_many_and_belongs_to_association
get :render_with_has_many_and_belongs_to_association
- assert_template 'projects/_project'
- assert_equal assigns(:developer).projects.map(&:name).join, @response.body
+ assert_equal Developer.find(1).projects.map(&:name).join, @response.body
end
def test_rendering_partial_with_has_many_association
get :render_with_has_many_association
- assert_template 'replies/_reply'
assert_equal 'Birdman is better!', @response.body
end
def test_rendering_partial_with_scope
get :render_with_scope
- assert_template 'replies/_reply'
assert_equal 'Birdman is better!Nuh uh!', @response.body
end
def test_render_with_record
get :render_with_record
- assert_template 'developers/_developer'
assert_equal 'David', @response.body
end
def test_render_with_record_collection
get :render_with_record_collection
- assert_template 'developers/_developer'
assert_equal 'DavidJamisfixture_3fixture_4fixture_5fixture_6fixture_7fixture_8fixture_9fixture_10Jamis', @response.body
end
def test_render_with_record_collection_and_spacer_template
get :render_with_record_collection_and_spacer_template
- assert_equal assigns(:developer).projects.map(&:name).join('only partial'), @response.body
+ assert_equal Developer.find(1).projects.map(&:name).join('only partial'), @response.body
end
def test_rendering_partial_with_has_one_association
mascot = Company.find(1).mascot
get :render_with_has_one_association
- assert_template 'mascots/_mascot'
assert_equal mascot.name, @response.body
end
end
@@ -130,13 +124,11 @@ class RenderPartialWithRecordIdentificationAndNestedControllersTest < ActiveReco
def test_render_with_record_in_nested_controller
get :render_with_record_in_nested_controller
- assert_template %r{\Afun/games/_game\Z}
assert_equal "Fun Pong\n", @response.body
end
def test_render_with_record_collection_in_nested_controller
get :render_with_record_collection_in_nested_controller
- assert_template %r{\Afun/games/_game\Z}
assert_equal "Fun Pong\nFun Tank\n", @response.body
end
end
@@ -149,7 +141,6 @@ class RenderPartialWithRecordIdentificationAndNestedControllersWithoutPrefixTest
ActionView::Base.prefix_partial_path_with_controller_namespace = false
get :render_with_record_in_nested_controller
- assert_template %r{\Agames/_game\Z}
assert_equal "Just Pong\n", @response.body
ensure
ActionView::Base.prefix_partial_path_with_controller_namespace = old_config
@@ -160,7 +151,6 @@ class RenderPartialWithRecordIdentificationAndNestedControllersWithoutPrefixTest
ActionView::Base.prefix_partial_path_with_controller_namespace = false
get :render_with_record_collection_in_nested_controller
- assert_template %r{\Agames/_game\Z}
assert_equal "Just Pong\nJust Tank\n", @response.body
ensure
ActionView::Base.prefix_partial_path_with_controller_namespace = old_config
@@ -172,13 +162,11 @@ class RenderPartialWithRecordIdentificationAndNestedDeeperControllersTest < Acti
def test_render_with_record_in_deeper_nested_controller
get :render_with_record_in_deeper_nested_controller
- assert_template %r{\Afun/serious/games/_game\Z}
assert_equal "Serious Chess\n", @response.body
end
def test_render_with_record_collection_in_deeper_nested_controller
get :render_with_record_collection_in_deeper_nested_controller
- assert_template %r{\Afun/serious/games/_game\Z}
assert_equal "Serious Chess\nSerious Sudoku\nSerious Solitaire\n", @response.body
end
end
@@ -191,7 +179,6 @@ class RenderPartialWithRecordIdentificationAndNestedDeeperControllersWithoutPref
ActionView::Base.prefix_partial_path_with_controller_namespace = false
get :render_with_record_in_deeper_nested_controller
- assert_template %r{\Agames/_game\Z}
assert_equal "Just Chess\n", @response.body
ensure
ActionView::Base.prefix_partial_path_with_controller_namespace = old_config
@@ -202,7 +189,6 @@ class RenderPartialWithRecordIdentificationAndNestedDeeperControllersWithoutPref
ActionView::Base.prefix_partial_path_with_controller_namespace = false
get :render_with_record_collection_in_deeper_nested_controller
- assert_template %r{\Agames/_game\Z}
assert_equal "Just Chess\nJust Sudoku\nJust Solitaire\n", @response.body
ensure
ActionView::Base.prefix_partial_path_with_controller_namespace = old_config
diff --git a/actionview/test/fixtures/actionpack/layout_tests/alt/layouts/alt.erb b/actionview/test/fixtures/actionpack/layout_tests/alt/layouts/alt.erb
index e69de29bb2..60b81525b5 100644
--- a/actionview/test/fixtures/actionpack/layout_tests/alt/layouts/alt.erb
+++ b/actionview/test/fixtures/actionpack/layout_tests/alt/layouts/alt.erb
@@ -0,0 +1 @@
+alt.erb
diff --git a/actionview/test/fixtures/actionpack/test/hyphen-ated.erb b/actionview/test/fixtures/actionpack/test/hyphen-ated.erb
index cd0875583a..28dbe94ee1 100644
--- a/actionview/test/fixtures/actionpack/test/hyphen-ated.erb
+++ b/actionview/test/fixtures/actionpack/test/hyphen-ated.erb
@@ -1 +1 @@
-Hello world!
+hyphen-ated.erb \ No newline at end of file
diff --git a/actionview/test/fixtures/test/_partial_name_in_local_assigns.erb b/actionview/test/fixtures/test/_partial_name_in_local_assigns.erb
new file mode 100644
index 0000000000..28ee9f41c5
--- /dev/null
+++ b/actionview/test/fixtures/test/_partial_name_in_local_assigns.erb
@@ -0,0 +1 @@
+<%= local_assigns.has_key?(:partial_name_in_local_assigns) %> \ No newline at end of file
diff --git a/actionview/test/template/atom_feed_helper_test.rb b/actionview/test/template/atom_feed_helper_test.rb
index 525d99750d..591cd71404 100644
--- a/actionview/test/template/atom_feed_helper_test.rb
+++ b/actionview/test/template/atom_feed_helper_test.rb
@@ -14,7 +14,7 @@ class ScrollsController < ActionController::Base
FEEDS["defaults"] = <<-EOT
atom_feed(:schema_date => '2008') do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll) do |entry|
@@ -31,7 +31,7 @@ class ScrollsController < ActionController::Base
FEEDS["entry_options"] = <<-EOT
atom_feed do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll, :url => "/otherstuff/" + scroll.to_param.to_s, :updated => Time.utc(2007, 1, scroll.id)) do |entry|
@@ -48,7 +48,7 @@ class ScrollsController < ActionController::Base
FEEDS["entry_type_options"] = <<-EOT
atom_feed(:schema_date => '2008') do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll, :type => 'text/xml') do |entry|
@@ -65,7 +65,7 @@ class ScrollsController < ActionController::Base
FEEDS["entry_url_false_option"] = <<-EOT
atom_feed do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll, :url => false) do |entry|
@@ -82,7 +82,7 @@ class ScrollsController < ActionController::Base
FEEDS["xml_block"] = <<-EOT
atom_feed do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
feed.author do |author|
author.name("DHH")
@@ -100,7 +100,7 @@ class ScrollsController < ActionController::Base
atom_feed({'xmlns:app' => 'http://www.w3.org/2007/app',
'xmlns:openSearch' => 'http://a9.com/-/spec/opensearch/1.1/'}) do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll) do |entry|
@@ -118,7 +118,7 @@ class ScrollsController < ActionController::Base
FEEDS["feed_with_overridden_ids"] = <<-EOT
atom_feed({:id => 'tag:test.rubyonrails.org,2008:test/'}) do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll, :id => "tag:test.rubyonrails.org,2008:"+scroll.id.to_s) do |entry|
@@ -137,7 +137,7 @@ class ScrollsController < ActionController::Base
atom_feed(:schema_date => '2008',
:instruct => {'xml-stylesheet' => { :href=> 't.css', :type => 'text/css' }}) do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll) do |entry|
@@ -155,7 +155,7 @@ class ScrollsController < ActionController::Base
atom_feed(:schema_date => '2008',
:instruct => {'target1' => [{ :a => '1', :b => '2' }, { :c => '3', :d => '4' }]}) do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll) do |entry|
@@ -172,7 +172,7 @@ class ScrollsController < ActionController::Base
FEEDS["feed_with_xhtml_content"] = <<-'EOT'
atom_feed do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll) do |entry|
@@ -197,7 +197,7 @@ class ScrollsController < ActionController::Base
new_xml = Builder::XmlMarkup.new(:target=>'')
atom_feed(:xml => new_xml) do |feed|
feed.title("My great blog!")
- feed.updated((@scrolls.first.created_at))
+ feed.updated(@scrolls.first.created_at)
@scrolls.each do |scroll|
feed.entry(scroll) do |entry|
diff --git a/actionview/test/template/form_options_helper_test.rb b/actionview/test/template/form_options_helper_test.rb
index d25fa3706f..d7daba8bf3 100644
--- a/actionview/test/template/form_options_helper_test.rb
+++ b/actionview/test/template/form_options_helper_test.rb
@@ -645,6 +645,13 @@ class FormOptionsHelperTest < ActionView::TestCase
)
end
+ def test_select_with_include_blank_false_and_required
+ @post = Post.new
+ @post.category = "<mus>"
+ e = assert_raises(ArgumentError) { select("post", "category", %w( abe <mus> hest), { include_blank: false }, required: 'required') }
+ assert_match(/include_blank cannot be false for a required field./, e.message)
+ end
+
def test_select_with_blank_as_string
@post = Post.new
@post.category = "<mus>"
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index 22665b6844..27bbb9b6c1 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -277,6 +277,14 @@ module RenderTestCases
assert_nil @view.render(:partial => "test/customer", :collection => nil)
end
+ def test_render_partial_without_object_does_not_put_partial_name_to_local_assigns
+ assert_equal 'false', @view.render(partial: 'test/partial_name_in_local_assigns')
+ end
+
+ def test_render_partial_with_nil_object_puts_partial_name_to_local_assigns
+ assert_equal 'true', @view.render(partial: 'test/partial_name_in_local_assigns', object: nil)
+ end
+
def test_render_partial_with_nil_values_in_collection
assert_equal "Hello: davidHello: Anonymous", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), nil ])
end
diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb
index c6cc47fb4f..05c3dc0613 100644
--- a/actionview/test/template/test_case_test.rb
+++ b/actionview/test/template/test_case_test.rb
@@ -306,64 +306,6 @@ module ActionView
end
end
- class RenderTemplateTest < ActionView::TestCase
- test "supports specifying templates with a Regexp" do
- controller.controller_path = "fun"
- render(:template => "fun/games/hello_world")
- assert_template %r{\Afun/games/hello_world\Z}
- end
-
- test "supports specifying partials" do
- controller.controller_path = "test"
- render(:template => "test/calling_partial_with_layout")
- assert_template :partial => "_partial_for_use_in_layout"
- end
-
- test "supports specifying locals (passing)" do
- controller.controller_path = "test"
- render(:template => "test/calling_partial_with_layout")
- assert_template :partial => "_partial_for_use_in_layout", :locals => { :name => "David" }
- end
-
- test "supports specifying locals (failing)" do
- controller.controller_path = "test"
- render(:template => "test/calling_partial_with_layout")
- e = assert_raise ActiveSupport::TestCase::Assertion do
- assert_template :partial => "_partial_for_use_in_layout", :locals => { :name => "Somebody Else" }
- end
- assert_match(/Somebody Else.*David/m, e.message)
- end
-
- test 'supports different locals on the same partial' do
- controller.controller_path = "test"
- render(:template => "test/render_two_partials")
- assert_template partial: '_partial', locals: { 'first' => '1' }
- assert_template partial: '_partial', locals: { 'second' => '2' }
- end
-
- test 'raises descriptive error message when template was not rendered' do
- controller.controller_path = "test"
- render(template: "test/hello_world_with_partial")
- e = assert_raise ActiveSupport::TestCase::Assertion do
- assert_template partial: 'i_was_never_rendered', locals: { 'did_not' => 'happen' }
- end
- assert_match "i_was_never_rendered to be rendered but it was not.", e.message
- assert_match 'Expected ["/test/partial"] to include "i_was_never_rendered"', e.message
- end
-
- test 'specifying locals works when the partial is inside a directory with underline prefix' do
- controller.controller_path = "test"
- render(template: 'test/render_partial_inside_directory')
- assert_template partial: 'test/_directory/_partial_with_locales', locals: { 'name' => 'Jane' }
- end
-
- test 'specifying locals works when the partial is inside a directory without underline prefix' do
- controller.controller_path = "test"
- render(template: 'test/render_partial_inside_directory')
- assert_template partial: 'test/_directory/partial_with_locales', locals: { 'name' => 'Jane' }
- end
- end
-
module AHelperWithInitialize
def initialize(*)
super
diff --git a/activejob/CHANGELOG.md b/activejob/CHANGELOG.md
index 1c55c1a4b8..c523723fc4 100644
--- a/activejob/CHANGELOG.md
+++ b/activejob/CHANGELOG.md
@@ -1,10 +1,17 @@
+* Allow `DelayedJob`, `Sidekiq`, `qu`, and `que` to report the job id back to
+ `ActiveJob::Base` as `provider_job_id`.
+
+ Fixes #18821.
+
+ *Kevin Deisz*, *Jeroen van Baarsen*
+
* `assert_enqueued_jobs` and `assert_performed_jobs` in block form use the
given number as expected value. This makes the error message much easier to
understand.
*y-yagi*
-* A generated job now inherents from `app/jobs/application_job.rb` by default.
+* A generated job now inherits from `app/jobs/application_job.rb` by default.
*Jeroen van Baarsen*
diff --git a/activejob/README.md b/activejob/README.md
index 5170ebee6e..f9a3183b1a 100644
--- a/activejob/README.md
+++ b/activejob/README.md
@@ -102,7 +102,7 @@ see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails.
The latest version of Active Job can be installed with RubyGems:
```
- % [sudo] gem install activejob
+ % gem install activejob
```
Source code can be downloaded as part of the Rails project on GitHub
diff --git a/activejob/Rakefile b/activejob/Rakefile
index 0e36bb81b3..8c86df3c91 100644
--- a/activejob/Rakefile
+++ b/activejob/Rakefile
@@ -1,5 +1,4 @@
require 'rake/testtask'
-require 'rubygems/package_task'
ACTIVEJOB_ADAPTERS = %w(inline delayed_job qu que queue_classic resque sidekiq sneakers sucker_punch backburner test)
ACTIVEJOB_ADAPTERS -= %w(queue_classic) if defined?(JRUBY_VERSION)
@@ -74,17 +73,3 @@ def run_without_aborting(tasks)
abort "Errors running #{errors.join(', ')}" if errors.any?
end
-
-
-spec = eval(File.read('activejob.gemspec'))
-
-Gem::PackageTask.new(spec) do |p|
- p.gem_spec = spec
-end
-
-desc 'Release to rubygems'
-task release: :package do
- require 'rake/gemcutter'
- Rake::Gemcutter::Tasks.new(spec).define
- Rake::Task['gem:push'].invoke
-end
diff --git a/activejob/lib/active_job/core.rb b/activejob/lib/active_job/core.rb
index acdfcdc791..0528572cd0 100644
--- a/activejob/lib/active_job/core.rb
+++ b/activejob/lib/active_job/core.rb
@@ -17,6 +17,9 @@ module ActiveJob
# Queue in which the job will reside.
attr_writer :queue_name
+
+ # ID optionally provided by adapter
+ attr_accessor :provider_job_id
end
# These methods will be included into any Active Job object, adding
diff --git a/activejob/lib/active_job/queue_adapter.rb b/activejob/lib/active_job/queue_adapter.rb
index 9c4519432d..457015b741 100644
--- a/activejob/lib/active_job/queue_adapter.rb
+++ b/activejob/lib/active_job/queue_adapter.rb
@@ -4,7 +4,7 @@ require 'active_support/core_ext/string/inflections'
module ActiveJob
# The <tt>ActiveJob::QueueAdapter</tt> module is used to load the
- # correct adapter. The default queue adapter is the :inline queue.
+ # correct adapter. The default queue adapter is the +:inline+ queue.
module QueueAdapter #:nodoc:
extend ActiveSupport::Concern
@@ -15,12 +15,14 @@ module ActiveJob
# Includes the setter method for changing the active queue adapter.
module ClassMethods
+ # Returns the backend queue provider. The default queue adapter
+ # is the +:inline+ queue. See QueueAdapters for more information.
def queue_adapter
_queue_adapter
end
# Specify the backend queue provider. The default queue adapter
- # is the :inline queue. See QueueAdapters for more
+ # is the +:inline+ queue. See QueueAdapters for more
# information.
def queue_adapter=(name_or_adapter_or_class)
self._queue_adapter = interpret_adapter(name_or_adapter_or_class)
diff --git a/activejob/lib/active_job/queue_adapters.rb b/activejob/lib/active_job/queue_adapters.rb
index bd69e525bb..1335e3236e 100644
--- a/activejob/lib/active_job/queue_adapters.rb
+++ b/activejob/lib/active_job/queue_adapters.rb
@@ -116,6 +116,10 @@ module ActiveJob
private_constant :ADAPTER
class << self
+ # Returns adapter for specified name.
+ #
+ # ActiveJob::QueueAdapters.lookup(:sidekiq)
+ # # => ActiveJob::QueueAdapters::SidekiqAdapter
def lookup(name)
const_get(name.to_s.camelize << ADAPTER)
end
diff --git a/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb b/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb
index 852a6ee326..ac83da2b9c 100644
--- a/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/delayed_job_adapter.rb
@@ -14,11 +14,15 @@ module ActiveJob
# Rails.application.config.active_job.queue_adapter = :delayed_job
class DelayedJobAdapter
def enqueue(job) #:nodoc:
- Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name)
+ delayed_job = Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name)
+ job.provider_job_id = delayed_job.id
+ delayed_job
end
def enqueue_at(job, timestamp) #:nodoc:
- Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name, run_at: Time.at(timestamp))
+ delayed_job = Delayed::Job.enqueue(JobWrapper.new(job.serialize), queue: job.queue_name, run_at: Time.at(timestamp))
+ job.provider_job_id = delayed_job.id
+ delayed_job
end
class JobWrapper #:nodoc:
diff --git a/activejob/lib/active_job/queue_adapters/qu_adapter.rb b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
index 36f4c14911..0e198922fc 100644
--- a/activejob/lib/active_job/queue_adapters/qu_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/qu_adapter.rb
@@ -17,9 +17,13 @@ module ActiveJob
# Rails.application.config.active_job.queue_adapter = :qu
class QuAdapter
def enqueue(job, *args) #:nodoc:
- Qu::Payload.new(klass: JobWrapper, args: [job.serialize]).tap do |payload|
+ qu_job = Qu::Payload.new(klass: JobWrapper, args: [job.serialize]).tap do |payload|
payload.instance_variable_set(:@queue, job.queue_name)
end.push
+
+ # qu_job can be nil depending on the configured backend
+ job.provider_job_id = qu_job.id unless qu_job.nil?
+ qu_job
end
def enqueue_at(job, timestamp, *args) #:nodoc:
diff --git a/activejob/lib/active_job/queue_adapters/que_adapter.rb b/activejob/lib/active_job/queue_adapters/que_adapter.rb
index a1a41ccc32..90947aa98d 100644
--- a/activejob/lib/active_job/queue_adapters/que_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/que_adapter.rb
@@ -16,11 +16,15 @@ module ActiveJob
# Rails.application.config.active_job.queue_adapter = :que
class QueAdapter
def enqueue(job) #:nodoc:
- JobWrapper.enqueue job.serialize
+ que_job = JobWrapper.enqueue job.serialize
+ job.provider_job_id = que_job.attrs["job_id"]
+ que_job
end
def enqueue_at(job, timestamp) #:nodoc:
- JobWrapper.enqueue job.serialize, run_at: Time.at(timestamp)
+ que_job = JobWrapper.enqueue job.serialize, run_at: Time.at(timestamp)
+ job.provider_job_id = que_job.attrs["job_id"]
+ que_job
end
class JobWrapper < Que::Job #:nodoc:
diff --git a/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb b/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb
index 743d5ea333..c321776bf5 100644
--- a/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb
+++ b/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb
@@ -17,7 +17,7 @@ module ActiveJob
class SidekiqAdapter
def enqueue(job) #:nodoc:
#Sidekiq::Client does not support symbols as keys
- Sidekiq::Client.push \
+ job.provider_job_id = Sidekiq::Client.push \
'class' => JobWrapper,
'wrapped' => job.class.to_s,
'queue' => job.queue_name,
@@ -25,7 +25,7 @@ module ActiveJob
end
def enqueue_at(job, timestamp) #:nodoc:
- Sidekiq::Client.push \
+ job.provider_job_id = Sidekiq::Client.push \
'class' => JobWrapper,
'wrapped' => job.class.to_s,
'queue' => job.queue_name,
diff --git a/activejob/test/cases/test_case_test.rb b/activejob/test/cases/test_case_test.rb
index 0a3a20d5a0..ee816e1dd5 100644
--- a/activejob/test/cases/test_case_test.rb
+++ b/activejob/test/cases/test_case_test.rb
@@ -5,11 +5,11 @@ require 'jobs/nested_job'
class ActiveJobTestCaseTest < ActiveJob::TestCase
# this tests that this job class doesn't get its adapter set.
- # that's the correct behaviour since we don't want to break
- # the `class_attribute` inheritence
- class TestClassAttributeInheritenceJob < ActiveJob::Base
+ # that's the correct behavior since we don't want to break
+ # the `class_attribute` inheritance
+ class TestClassAttributeInheritanceJob < ActiveJob::Base
def self.queue_adapter=(*)
- raise 'Attemping to break `class_attribute` inheritence, bad!'
+ raise 'Attemping to break `class_attribute` inheritance, bad!'
end
end
diff --git a/activejob/test/helper.rb b/activejob/test/helper.rb
index 72ec2b8904..57907042d9 100644
--- a/activejob/test/helper.rb
+++ b/activejob/test/helper.rb
@@ -14,5 +14,3 @@ else
end
require 'active_support/testing/autorun'
-
-ActiveSupport::TestCase.test_order = :random
diff --git a/activejob/test/integration/queuing_test.rb b/activejob/test/integration/queuing_test.rb
index 96794ffef3..d345092dee 100644
--- a/activejob/test/integration/queuing_test.rb
+++ b/activejob/test/integration/queuing_test.rb
@@ -11,7 +11,7 @@ class QueuingTest < ActiveSupport::TestCase
end
test 'should not run jobs queued on a non-listening queue' do
- skip if adapter_is?(:inline) || adapter_is?(:sucker_punch) || adapter_is?(:que)
+ skip if adapter_is?(:inline, :sucker_punch, :que)
old_queue = TestJob.queue_name
begin
@@ -55,4 +55,17 @@ class QueuingTest < ActiveSupport::TestCase
skip
end
end
+
+ test 'should supply a provider_job_id when available for immediate jobs' do
+ skip unless adapter_is?(:delayed_job, :sidekiq, :qu, :que)
+ test_job = TestJob.perform_later @id
+ refute test_job.provider_job_id.nil?, 'Provider job id should be set by provider'
+ end
+
+ test 'should supply a provider_job_id when available for delayed jobs' do
+ skip unless adapter_is?(:delayed_job, :sidekiq, :que)
+ delayed_test_job = TestJob.set(wait: 1.minute).perform_later @id
+ refute delayed_test_job.provider_job_id.nil?,
+ 'Provider job id should by set for delayed jobs by provider'
+ end
end
diff --git a/activejob/test/support/integration/test_case_helpers.rb b/activejob/test/support/integration/test_case_helpers.rb
index bed28b2900..7e87ede275 100644
--- a/activejob/test/support/integration/test_case_helpers.rb
+++ b/activejob/test/support/integration/test_case_helpers.rb
@@ -27,8 +27,8 @@ module TestCaseHelpers
jobs_manager.clear_jobs
end
- def adapter_is?(adapter_class_symbol)
- ActiveJob::Base.queue_adapter.class.name.split("::").last.gsub(/Adapter$/, '').underscore == adapter_class_symbol.to_s
+ def adapter_is?(*adapter_class_symbols)
+ adapter_class_symbols.map(&:to_s).include?(ActiveJob::Base.queue_adapter.class.name.split("::").last.gsub(/Adapter$/, '').underscore)
end
def wait_for_jobs_to_finish_for(seconds=60)
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 3c1510b31d..dddfd940bb 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -23,7 +23,7 @@
*Wojciech Wnętrzak*
* Deprecate `ActiveModel::Errors#get`, `ActiveModel::Errors#set` and
- `ActiveModel::Errors#[]=` methods that have inconsistent behaviour.
+ `ActiveModel::Errors#[]=` methods that have inconsistent behavior.
*Wojciech Wnętrzak*
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc
index 4920666f27..5c36b1277e 100644
--- a/activemodel/README.rdoc
+++ b/activemodel/README.rdoc
@@ -242,7 +242,7 @@ behavior out of the box:
The latest version of Active Model can be installed with RubyGems:
- % [sudo] gem install activemodel
+ % gem install activemodel
Source code can be downloaded as part of the Rails project on GitHub
diff --git a/activemodel/Rakefile b/activemodel/Rakefile
index 7256285a41..5a67f0a151 100644
--- a/activemodel/Rakefile
+++ b/activemodel/Rakefile
@@ -1,7 +1,7 @@
-dir = File.dirname(__FILE__)
-
require 'rake/testtask'
+dir = File.dirname(__FILE__)
+
task :default => :test
Rake::TestTask.new do |t|
@@ -19,18 +19,3 @@ namespace :test do
end or raise "Failures"
end
end
-
-require 'rubygems/package_task'
-
-spec = eval(File.read("#{dir}/activemodel.gemspec"))
-
-Gem::PackageTask.new(spec) do |p|
- p.gem_spec = spec
-end
-
-desc "Release to rubygems"
-task :release => :package do
- require 'rake/gemcutter'
- Rake::Gemcutter::Tasks.new(spec).define
- Rake::Task['gem:push'].invoke
-end
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index ff7280f9e5..286cd6c206 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -225,7 +225,7 @@ module ActiveModel
end
# Declares the attributes that should be prefixed and suffixed by
- # ActiveModel::AttributeMethods.
+ # <tt>ActiveModel::AttributeMethods</tt>.
#
# To use, pass attribute names (as strings or symbols). Be sure to declare
# +define_attribute_methods+ after you define any prefix, suffix or affix
@@ -253,7 +253,7 @@ module ActiveModel
end
# Declares an attribute that should be prefixed and suffixed by
- # ActiveModel::AttributeMethods.
+ # <tt>ActiveModel::AttributeMethods</tt>.
#
# To use, pass an attribute name (as string or symbol). Be sure to declare
# +define_attribute_method+ after you define any prefix, suffix or affix
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index c0fc507286..0169c20e0b 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -102,10 +102,10 @@ module ActiveModel
# person.changes # => {"name" => ["Bill", "Bob"]}
#
# If an attribute is modified in-place then make use of
- # +[attribute_name]_will_change!+ to mark that the attribute is changing.
+ # <tt>[attribute_name]_will_change!</tt> to mark that the attribute is changing.
# Otherwise \Active \Model can't track changes to in-place attributes. Note
# that Active Record can detect in-place modifications automatically. You do
- # not need to call +[attribute_name]_will_change!+ on Active Record models.
+ # not need to call <tt>[attribute_name]_will_change!</tt> on Active Record models.
#
# person.name_will_change!
# person.name_change # => ["Bill", "Bill"]
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 287a2559d2..29e0c977ce 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -43,11 +43,11 @@ module ActiveModel
# end
# end
#
- # The last three methods are required in your object for Errors to be
+ # The last three methods are required in your object for +Errors+ to be
# able to generate error messages correctly and also handle multiple
- # languages. Of course, if you extend your object with ActiveModel::Translation
+ # languages. Of course, if you extend your object with <tt>ActiveModel::Translation</tt>
# you will not need to implement the last two. Likewise, using
- # ActiveModel::Validations will handle the validation related methods
+ # <tt>ActiveModel::Validations</tt> will handle the validation related methods
# for you.
#
# The above allows you to do:
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index c06a64c75f..f95849eb84 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -94,6 +94,37 @@ module ActiveModel
# person.serializable_hash(except: :name) # => {"age"=>22}
# person.serializable_hash(methods: :capitalized_name)
# # => {"name"=>"bob", "age"=>22, "capitalized_name"=>"Bob"}
+ #
+ # Example with <tt>:include</tt> option
+ #
+ # class User
+ # include ActiveModel::Serializers::JSON
+ # attr_accessor :name, :notes # Emulate has_many :notes
+ # def attributes
+ # {'name' => nil}
+ # end
+ # end
+ #
+ # class Note
+ # include ActiveModel::Serializers::JSON
+ # attr_accessor :title, :text
+ # def attributes
+ # {'title' => nil, 'text' => nil}
+ # end
+ # end
+ #
+ # note = Note.new
+ # note.title = 'Battle of Austerlitz'
+ # note.text = 'Some text here'
+ #
+ # user = User.new
+ # user.name = 'Napoleon'
+ # user.notes = [note]
+ #
+ # user.serializable_hash
+ # # => {"name" => "Napoleon"}
+ # user.serializable_hash(include: { notes: { only: 'title' }})
+ # # => {"name" => "Napoleon", "notes" => [{"title"=>"Battle of Austerlitz"}]}
def serializable_hash(options = nil)
options ||= {}
diff --git a/activemodel/test/cases/attribute_assignment_test.rb b/activemodel/test/cases/attribute_assignment_test.rb
index 3b01644dd1..64a85e01eb 100644
--- a/activemodel/test/cases/attribute_assignment_test.rb
+++ b/activemodel/test/cases/attribute_assignment_test.rb
@@ -58,8 +58,6 @@ class AttributeAssignmentTest < ActiveModel::TestCase
end
test "assign private attribute" do
- rubinius_skip "https://github.com/rubinius/rubinius/issues/3328"
-
model = Model.new
assert_raises(ActiveModel::UnknownAttributeError) do
model.assign_attributes(metadata: { a: 1 })
diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb
index d10c20caad..3fa63917d0 100644
--- a/activemodel/test/cases/errors_test.rb
+++ b/activemodel/test/cases/errors_test.rb
@@ -27,6 +27,14 @@ class ErrorsTest < ActiveModel::TestCase
end
end
+ def setup
+ @mock_generator = MiniTest::Mock.new
+ end
+
+ def teardown
+ @mock_generator.verify
+ end
+
def test_delete
errors = ActiveModel::Errors.new(self)
errors[:foo] << 'omg'
@@ -299,60 +307,74 @@ class ErrorsTest < ActiveModel::TestCase
test "add_on_empty generates message" do
person = Person.new
- person.errors.expects(:generate_message).with(:name, :empty, {})
- assert_deprecated do
- person.errors.add_on_empty :name
+ @mock_generator.expect(:call, nil, [:name, :empty, {}])
+ person.errors.stub(:generate_message, @mock_generator) do
+ assert_deprecated do
+ person.errors.add_on_empty :name
+ end
end
end
test "add_on_empty generates message for multiple attributes" do
person = Person.new
- person.errors.expects(:generate_message).with(:name, :empty, {})
- person.errors.expects(:generate_message).with(:age, :empty, {})
- assert_deprecated do
- person.errors.add_on_empty [:name, :age]
+ @mock_generator.expect(:call, nil, [:name, :empty, {}])
+ @mock_generator.expect(:call, nil, [:age, :empty, {}])
+ person.errors.stub(:generate_message, @mock_generator) do
+ assert_deprecated do
+ person.errors.add_on_empty [:name, :age]
+ end
end
end
test "add_on_empty generates message with custom default message" do
person = Person.new
- person.errors.expects(:generate_message).with(:name, :empty, { message: 'custom' })
- assert_deprecated do
- person.errors.add_on_empty :name, message: 'custom'
+ @mock_generator.expect(:call, nil, [:name, :empty, { message: 'custom' }])
+ person.errors.stub(:generate_message, @mock_generator) do
+ assert_deprecated do
+ person.errors.add_on_empty :name, message: 'custom'
+ end
end
end
test "add_on_empty generates message with empty string value" do
person = Person.new
person.name = ''
- person.errors.expects(:generate_message).with(:name, :empty, {})
- assert_deprecated do
- person.errors.add_on_empty :name
+ @mock_generator.expect(:call, nil, [:name, :empty, {}])
+ person.errors.stub(:generate_message, @mock_generator) do
+ assert_deprecated do
+ person.errors.add_on_empty :name
+ end
end
end
test "add_on_blank generates message" do
person = Person.new
- person.errors.expects(:generate_message).with(:name, :blank, {})
- assert_deprecated do
- person.errors.add_on_blank :name
+ @mock_generator.expect(:call, nil, [:name, :blank, {}])
+ person.errors.stub(:generate_message, @mock_generator) do
+ assert_deprecated do
+ person.errors.add_on_blank :name
+ end
end
end
test "add_on_blank generates message for multiple attributes" do
person = Person.new
- person.errors.expects(:generate_message).with(:name, :blank, {})
- person.errors.expects(:generate_message).with(:age, :blank, {})
- assert_deprecated do
- person.errors.add_on_blank [:name, :age]
+ @mock_generator.expect(:call, nil, [:name, :blank, {}])
+ @mock_generator.expect(:call, nil, [:age, :blank, {}])
+ person.errors.stub(:generate_message, @mock_generator) do
+ assert_deprecated do
+ person.errors.add_on_blank [:name, :age]
+ end
end
end
test "add_on_blank generates message with custom default message" do
person = Person.new
- person.errors.expects(:generate_message).with(:name, :blank, { message: 'custom' })
- assert_deprecated do
- person.errors.add_on_blank :name, message: 'custom'
+ @mock_generator.expect(:call, nil, [:name, :blank, { message: 'custom' }])
+ person.errors.stub(:generate_message, @mock_generator) do
+ assert_deprecated do
+ person.errors.add_on_blank :name, message: 'custom'
+ end
end
end
diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb
index 2b9de5e5d2..0d179ea9ad 100644
--- a/activemodel/test/cases/helper.rb
+++ b/activemodel/test/cases/helper.rb
@@ -12,7 +12,7 @@ I18n.enforce_available_locales = false
require 'active_support/testing/autorun'
-require 'mocha/setup' # FIXME: stop using mocha
+require 'minitest/mock'
# Skips the current run on Rubinius using Minitest::Assertions#skip
def rubinius_skip(message = '')
diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb
index 22fca5bd17..37faf6cef8 100644
--- a/activemodel/test/cases/serializers/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializers/xml_serialization_test.rb
@@ -2,6 +2,7 @@ require 'cases/helper'
require 'models/contact'
require 'active_support/core_ext/object/instance_variables'
require 'ostruct'
+require 'yaml'
module Admin
class Contact < ::Contact
diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb
index 70ee7afecc..70b93a202b 100644
--- a/activemodel/test/cases/validations/i18n_validation_test.rb
+++ b/activemodel/test/cases/validations/i18n_validation_test.rb
@@ -11,6 +11,7 @@ class I18nValidationTest < ActiveModel::TestCase
I18n.load_path.clear
I18n.backend = I18n::Backend::Simple.new
I18n.backend.store_translations('en', errors: { messages: { custom: nil } })
+ @mock_generator = MiniTest::Mock.new
end
def teardown
@@ -18,6 +19,7 @@ class I18nValidationTest < ActiveModel::TestCase
I18n.load_path.replace @old_load_path
I18n.backend = @old_backend
I18n.backend.reload!
+ @mock_generator.verify
end
def test_full_message_encoding
@@ -30,8 +32,10 @@ class I18nValidationTest < ActiveModel::TestCase
def test_errors_full_messages_translates_human_attribute_name_for_model_attributes
@person.errors.add(:name, 'not found')
- Person.expects(:human_attribute_name).with(:name, default: 'Name').returns("Person's name")
- assert_equal ["Person's name not found"], @person.errors.full_messages
+ @mock_generator.expect(:call, "Person's name", [:name, default: 'Name'])
+ Person.stub(:human_attribute_name, @mock_generator) do
+ assert_equal ["Person's name not found"], @person.errors.full_messages
+ end
end
def test_errors_full_messages_uses_format
@@ -60,8 +64,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_confirmation_of on generated message #{name}" do
Person.validates_confirmation_of :title, validation_options
@person.title_confirmation = 'foo'
- @person.errors.expects(:generate_message).with(:title_confirmation, :confirmation, generate_message_options.merge(attribute: 'Title'))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title_confirmation, :confirmation, generate_message_options.merge(attribute: 'Title')])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -70,8 +76,10 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_acceptance_of on generated message #{name}" do
Person.validates_acceptance_of :title, validation_options.merge(allow_nil: false)
- @person.errors.expects(:generate_message).with(:title, :accepted, generate_message_options)
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :accepted, generate_message_options])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -80,18 +88,22 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_presence_of on generated message #{name}" do
Person.validates_presence_of :title, validation_options
- @person.errors.expects(:generate_message).with(:title, :blank, generate_message_options)
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :blank, generate_message_options])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
# validates_length_of :within too short w/ mocha
COMMON_CASES.each do |name, validation_options, generate_message_options|
- test "validates_length_of for :withing on generated message when too short #{name}" do
+ test "validates_length_of for :within on generated message when too short #{name}" do
Person.validates_length_of :title, validation_options.merge(within: 3..5)
- @person.errors.expects(:generate_message).with(:title, :too_short, generate_message_options.merge(count: 3))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :too_short, generate_message_options.merge(count: 3)])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -101,8 +113,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_length_of for :too_long generated message #{name}" do
Person.validates_length_of :title, validation_options.merge(within: 3..5)
@person.title = 'this title is too long'
- @person.errors.expects(:generate_message).with(:title, :too_long, generate_message_options.merge(count: 5))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :too_long, generate_message_options.merge(count: 5)])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -111,8 +125,10 @@ class I18nValidationTest < ActiveModel::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_length_of for :is on generated message #{name}" do
Person.validates_length_of :title, validation_options.merge(is: 5)
- @person.errors.expects(:generate_message).with(:title, :wrong_length, generate_message_options.merge(count: 5))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :wrong_length, generate_message_options.merge(count: 5)])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -122,8 +138,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_format_of on generated message #{name}" do
Person.validates_format_of :title, validation_options.merge(with: /\A[1-9][0-9]*\z/)
@person.title = '72x'
- @person.errors.expects(:generate_message).with(:title, :invalid, generate_message_options.merge(value: '72x'))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :invalid, generate_message_options.merge(value: '72x')])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -133,8 +151,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_inclusion_of on generated message #{name}" do
Person.validates_inclusion_of :title, validation_options.merge(in: %w(a b c))
@person.title = 'z'
- @person.errors.expects(:generate_message).with(:title, :inclusion, generate_message_options.merge(value: 'z'))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :inclusion, generate_message_options.merge(value: 'z')])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -144,8 +164,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_inclusion_of using :within on generated message #{name}" do
Person.validates_inclusion_of :title, validation_options.merge(within: %w(a b c))
@person.title = 'z'
- @person.errors.expects(:generate_message).with(:title, :inclusion, generate_message_options.merge(value: 'z'))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :inclusion, generate_message_options.merge(value: 'z')])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -155,8 +177,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_exclusion_of generated message #{name}" do
Person.validates_exclusion_of :title, validation_options.merge(in: %w(a b c))
@person.title = 'a'
- @person.errors.expects(:generate_message).with(:title, :exclusion, generate_message_options.merge(value: 'a'))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :exclusion, generate_message_options.merge(value: 'a')])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -166,8 +190,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_exclusion_of using :within generated message #{name}" do
Person.validates_exclusion_of :title, validation_options.merge(within: %w(a b c))
@person.title = 'a'
- @person.errors.expects(:generate_message).with(:title, :exclusion, generate_message_options.merge(value: 'a'))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :exclusion, generate_message_options.merge(value: 'a')])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -177,8 +203,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of generated message #{name}" do
Person.validates_numericality_of :title, validation_options
@person.title = 'a'
- @person.errors.expects(:generate_message).with(:title, :not_a_number, generate_message_options.merge(value: 'a'))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :not_a_number, generate_message_options.merge(value: 'a')])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -188,8 +216,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of for :only_integer on generated message #{name}" do
Person.validates_numericality_of :title, validation_options.merge(only_integer: true)
@person.title = '0.0'
- @person.errors.expects(:generate_message).with(:title, :not_an_integer, generate_message_options.merge(value: '0.0'))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :not_an_integer, generate_message_options.merge(value: '0.0')])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -199,8 +229,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of for :odd on generated message #{name}" do
Person.validates_numericality_of :title, validation_options.merge(only_integer: true, odd: true)
@person.title = 0
- @person.errors.expects(:generate_message).with(:title, :odd, generate_message_options.merge(value: 0))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :odd, generate_message_options.merge(value: 0)])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
@@ -210,8 +242,10 @@ class I18nValidationTest < ActiveModel::TestCase
test "validates_numericality_of for :less_than on generated message #{name}" do
Person.validates_numericality_of :title, validation_options.merge(only_integer: true, less_than: 0)
@person.title = 1
- @person.errors.expects(:generate_message).with(:title, :less_than, generate_message_options.merge(value: 1, count: 0))
- @person.valid?
+ @mock_generator.expect(:call, nil, [:title, :less_than, generate_message_options.merge(value: 1, count: 0)])
+ @person.errors.stub(:generate_message, @mock_generator) do
+ @person.valid?
+ end
end
end
diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb
index 01804032f0..9ee8b79da9 100644
--- a/activemodel/test/cases/validations/with_validation_test.rb
+++ b/activemodel/test/cases/validations/with_validation_test.rb
@@ -97,12 +97,14 @@ class ValidatesWithTest < ActiveModel::TestCase
test "passes all configuration options to the validator class" do
topic = Topic.new
- validator = mock()
- validator.expects(:new).with(foo: :bar, if: "1 == 1", class: Topic).returns(validator)
- validator.expects(:validate).with(topic)
+ validator = MiniTest::Mock.new
+ validator.expect(:new, validator, [{foo: :bar, if: "1 == 1", class: Topic}])
+ validator.expect(:validate, nil, [topic])
+ validator.expect(:is_a?, false, [Symbol])
Topic.validates_with(validator, if: "1 == 1", foo: :bar)
assert topic.valid?
+ validator.verify
end
test "validates_with with options" do
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 09045087d9..6680ef4bd2 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,6 +1,96 @@
+* Correctly handle decimal arrays with defaults in the schema dumper.
+
+ Fixes #20515.
+
+ *Sean Griffin & jmondo*
+
+* Deprecate the PG `:point` type in favor of a new one which will return
+ `Point` objects instead of an `Array`
+
+ *Sean Griffin*
+
+* Ensure symbols passed to `ActiveRecord::Relation#select` are always treated
+ as columns.
+
+ Fixes #20360.
+
+ *Sean Griffin*
+
+* Do not set `sql_mode` if `strict: :default` is specified.
+
+ ```
+ # database.yml
+ production:
+ adapter: mysql2
+ database: foo_prod
+ user: foo
+ strict: :default
+ ```
+
+ *Ryuta Kamizono*
+
+* Allow proc defaults to be passed to the attributes API. See documentation
+ for examples.
+
+ *Sean Griffin*, *Kir Shatrov*
+
+* SQLite: `:collation` support for string and text columns.
+
+ Example:
+
+ create_table :foo do |t|
+ t.string :string_nocase, collation: 'NOCASE'
+ t.text :text_rtrim, collation: 'RTRIM'
+ end
+
+ add_column :foo, :title, :string, collation: 'RTRIM'
+
+ change_column :foo, :title, :string, collation: 'NOCASE'
+
+ *Akshay Vishnoi*
+
+* Allow the use of symbols or strings to specify enum values in test
+ fixtures:
+
+ awdr:
+ title: "Agile Web Development with Rails"
+ status: :proposed
+
+ *George Claghorn*
+
+* Clear query cache when `ActiveRecord::Base#reload` is called.
+
+ *Shane Hender, Pierre Nespo*
+
+* Include stored procedures and function on the MySQL structure dump.
+
+ *Jonathan Worek*
+
+* Pass `:extend` option for `has_and_belongs_to_many` associations to the underlying `has_many :through`.
+
+ *Jaehyun Shin*
+
+* Deprecate `Relation#uniq` use `Relation#distinct` instead.
+
+ See #9683.
+
+ *Yves Senn*
+
+* Allow single table inheritance instantiation to work when storing
+ demodulized class names.
+
+ *Alex Robbin*
+
+* Correctly pass MySQL options when using `structure_dump` or
+ `structure_load`.
+
+ Specifically, it fixes an issue when using SSL authentication.
+
+ *Alex Coomans*
+
* Dump indexes in `create_table` instead of `add_index`.
- If the adapter supports indexes in create table, generated SQL is
+ If the adapter supports indexes in `create_table`, generated SQL is
slightly more efficient.
*Ryuta Kamizono*
@@ -211,7 +301,7 @@
*Josef Šimánek*
-* Fixed ActiveRecord::Relation#becomes! and changed_attributes issues for type
+* Fixed `ActiveRecord::Relation#becomes!` and `changed_attributes` issues for type
columns.
Fixes #17139.
@@ -394,8 +484,8 @@
*Henrik Nygren*
-* Fixed ActiveRecord::Relation#group method when an argument is an SQL
- reserved key word:
+* Fixed `ActiveRecord::Relation#group` method when an argument is an SQL
+ reserved keyword:
Example:
@@ -404,7 +494,7 @@
*Bogdan Gusiev*
-* Added the `#or` method on ActiveRecord::Relation, allowing use of the OR
+* Added the `#or` method on `ActiveRecord::Relation`, allowing use of the OR
operator to combine WHERE or HAVING clauses.
Example:
@@ -604,7 +694,7 @@
The preferred method to halt a callback chain from now on is to explicitly
`throw(:abort)`.
- In the past, returning `false` in an ActiveRecord `before_` callback had the
+ In the past, returning `false` in an Active Record `before_` callback had the
side effect of halting the callback chain.
This is not recommended anymore and, depending on the value of the
`config.active_support.halt_callback_chains_on_return_false` option, will
diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc
index f4777919d3..049c5d2b3b 100644
--- a/activerecord/README.rdoc
+++ b/activerecord/README.rdoc
@@ -31,8 +31,8 @@ which might look like this:
PRIMARY KEY (id)
);
-This would also define the following accessors: `Product#name` and
-`Product#name=(new_name)`.
+This would also define the following accessors: <tt>Product#name</tt> and
+<tt>Product#name=(new_name)</tt>.
* Associations between objects defined by simple class methods.
@@ -188,7 +188,7 @@ Admit the Database:
The latest version of Active Record can be installed with RubyGems:
- % [sudo] gem install activerecord
+ % gem install activerecord
Source code can be downloaded as part of the Rails project on GitHub:
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index f1facac21b..a619204e6f 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -1,5 +1,4 @@
require 'rake/testtask'
-require 'rubygems/package_task'
require File.expand_path(File.dirname(__FILE__)) + "/test/config"
require File.expand_path(File.dirname(__FILE__)) + "/test/support/config"
@@ -122,7 +121,7 @@ namespace :db do
# prepare hstore
if %x( createdb --version ).strip.gsub(/(.*)(\d\.\d\.\d)$/, "\\2") < "9.1.0"
- puts "Please prepare hstore data type. See http://www.postgresql.org/docs/9.0/static/hstore.html"
+ puts "Please prepare hstore data type. See http://www.postgresql.org/docs/current/static/hstore.html"
end
end
@@ -151,18 +150,3 @@ task :lines do
files = FileList["lib/active_record/**/*.rb"]
CodeTools::LineStatistics.new(files).print_loc
end
-
-spec = eval(File.read('activerecord.gemspec'))
-
-Gem::PackageTask.new(spec) do |p|
- p.gem_spec = spec
-end
-
-# Publishing ------------------------------------------------------
-
-desc "Release to rubygems"
-task :release => :package do
- require 'rake/gemcutter'
- Rake::Gemcutter::Tasks.new(spec).define
- Rake::Task['gem:push'].invoke
-end
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index 3d497a30fb..f7b50cd25a 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -142,7 +142,7 @@ module ActiveRecord
# converted to an instance of value class if necessary.
#
# For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that should be
- # aggregated using the NetAddr::CIDR value class (http://www.ruby-doc.org/gems/docs/n/netaddr-1.5.0/NetAddr/CIDR.html).
+ # aggregated using the NetAddr::CIDR value class (http://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
# The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
# New values can be assigned to the value object using either another NetAddr::CIDR object, a string
# or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index c5c2178ee2..1ca648d48d 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -185,7 +185,7 @@ module ActiveRecord
super
end
- # Returns the specified association instance if it responds to :loaded?, nil otherwise.
+ # Returns the specified association instance if it exists, nil otherwise.
def association_instance_get(name)
@association_cache[name]
end
@@ -266,7 +266,6 @@ module ActiveRecord
# others.find(*args) | X | X | X
# others.exists? | X | X | X
# others.distinct | X | X | X
- # others.uniq | X | X | X
# others.reset | X | X | X
#
# === Overriding generated methods
@@ -285,7 +284,7 @@ module ActiveRecord
# end
#
# If your model class is <tt>Project</tt>, the module is
- # named <tt>Project::GeneratedFeatureMethods</tt>. The GeneratedFeatureMethods module is
+ # named <tt>Project::GeneratedAssociationMethods</tt>. The GeneratedAssociationMethods module is
# included in the model class immediately after the (anonymous) generated attributes methods
# module, meaning an association will override the methods for an attribute with the same name.
#
@@ -1722,7 +1721,7 @@ module ActiveRecord
Builder::HasMany.define_callbacks self, middle_reflection
Reflection.add_reflection self, middle_reflection.name, middle_reflection
- middle_reflection.parent_reflection = [name.to_s, habtm_reflection]
+ middle_reflection.parent_reflection = habtm_reflection
include Module.new {
class_eval <<-RUBY, __FILE__, __LINE__ + 1
@@ -1738,12 +1737,12 @@ module ActiveRecord
hm_options[:through] = middle_reflection.name
hm_options[:source] = join_model.right_reflection.name
- [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name].each do |k|
+ [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend].each do |k|
hm_options[k] = options[k] if options.key? k
end
has_many name, scope, hm_options, &extension
- self._reflections[name.to_s].parent_reflection = [name.to_s, habtm_reflection]
+ self._reflections[name.to_s].parent_reflection = habtm_reflection
end
end
end
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 685c3a5f17..ddeafb40ea 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -470,15 +470,16 @@ module ActiveRecord
@association.destroy_all
end
- # Deletes the +records+ supplied and removes them from the collection. For
- # +has_many+ associations, the deletion is done according to the strategy
- # specified by the <tt>:dependent</tt> option. Returns an array with the
+ # Deletes the +records+ supplied from the collection according to the strategy
+ # specified by the +:dependent+ option. If no +:dependent+ option is given,
+ # then it will follow the default strategy. Returns an array with the
# deleted records.
#
- # If no <tt>:dependent</tt> option is given, then it will follow the default
- # strategy. The default strategy is <tt>:nullify</tt>. This sets the foreign
- # keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>, the default
- # strategy is +delete_all+.
+ # For +has_many :through+ associations, the default deletion strategy is
+ # +:delete_all+.
+ #
+ # For +has_many+ associations, the default deletion strategy is +:nullify+.
+ # This sets the foreign keys to +NULL+.
#
# class Person < ActiveRecord::Base
# has_many :pets # dependent: :nullify option by default
diff --git a/activerecord/lib/active_record/attribute/user_provided_default.rb b/activerecord/lib/active_record/attribute/user_provided_default.rb
new file mode 100644
index 0000000000..e0bee8c17e
--- /dev/null
+++ b/activerecord/lib/active_record/attribute/user_provided_default.rb
@@ -0,0 +1,32 @@
+require 'active_record/attribute'
+
+module ActiveRecord
+ class Attribute # :nodoc:
+ class UserProvidedDefault < FromUser
+ def initialize(name, value, type, database_default)
+ super(name, value, type)
+ @database_default = database_default
+ end
+
+ def type_cast(value)
+ if value.is_a?(Proc)
+ super(value.call)
+ else
+ super
+ end
+ end
+
+ def changed_in_place_from?(old_value)
+ super || changed_from?(database_default.value)
+ end
+
+ def with_type(type)
+ self.class.new(name, value_before_type_cast, type, database_default)
+ end
+
+ protected
+
+ attr_reader :database_default
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index cc265e2af6..45fdcaa1cd 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -29,7 +29,8 @@ module ActiveRecord
assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
end
- # Re-raise with the ActiveRecord constant in case of an error
+ # Tries to assign given value to given attribute.
+ # In case of an error, re-raises with the ActiveRecord constant.
def _assign_attribute(k, v) # :nodoc:
super
rescue ActiveModel::UnknownAttributeError
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index d0d8a968c5..fd7099e0de 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -11,6 +11,15 @@ module ActiveRecord
# serialized object must be of that class on assignment and retrieval.
# Otherwise <tt>SerializationTypeMismatch</tt> will be raised.
#
+ # Keep in mind that database adapters handle certain serialization tasks
+ # for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be
+ # converted between JSON object/array syntax and Ruby +Hash+ or +Array+
+ # objects transparently. There is no need to use +serialize+ in this
+ # case.
+ #
+ # For more complex cases, such as conversion to or from your application
+ # domain objects, consider using the ActiveRecord::Attributes API.
+ #
# ==== Parameters
#
# * +attr_name+ - The field name that should be serialized.
diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb
index 50339b6f69..8b2c4c7170 100644
--- a/activerecord/lib/active_record/attributes.rb
+++ b/activerecord/lib/active_record/attributes.rb
@@ -1,3 +1,5 @@
+require 'active_record/attribute/user_provided_default'
+
module ActiveRecord
# See ActiveRecord::Attributes::ClassMethods for documentation
module Attributes
@@ -80,6 +82,14 @@ module ActiveRecord
#
# StoreListing.new.my_string # => "new default"
#
+ # class Product < ActiveRecord::Base
+ # attribute :my_default_proc, :datetime, default: -> { Time.now }
+ # end
+ #
+ # Product.new.my_default_proc # => 2015-05-30 11:04:48 -0600
+ # sleep 1
+ # Product.new.my_default_proc # => 2015-05-30 11:04:49 -0600
+ #
# Attributes do not need to be backed by a database column.
#
# class MyModel < ActiveRecord::Base
@@ -202,7 +212,8 @@ module ActiveRecord
#
# +default+ The default value to use when no value is provided. If this option
# is not passed, the previous default value (if any) will be used.
- # Otherwise, the default will be +nil+.
+ # Otherwise, the default will be +nil+. A proc can also be passed, and
+ # will be called once each time a new value is needed.
#
# +user_provided_default+ Whether the default value should be cast using
# +cast+ or +deserialize+.
@@ -236,7 +247,12 @@ module ActiveRecord
if value == NO_DEFAULT_PROVIDED
default_attribute = _default_attributes[name].with_type(type)
elsif from_user
- default_attribute = Attribute.from_user(name, value, type)
+ default_attribute = Attribute::UserProvidedDefault.new(
+ name,
+ value,
+ type,
+ _default_attributes[name],
+ )
else
default_attribute = Attribute.from_database(name, value, type)
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 9c5b7d937d..c918e88590 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -5,7 +5,6 @@ require 'active_support/dependencies'
require 'active_support/descendants_tracker'
require 'active_support/time'
require 'active_support/core_ext/module/attribute_accessors'
-require 'active_support/core_ext/class/delegating_attributes'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/hash/deep_merge'
require 'active_support/core_ext/hash/slice'
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index 2fcba8e309..3027ce928e 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -194,7 +194,7 @@ module ActiveRecord
#
# If the +before_validation+ callback throws +:abort+, the process will be
# aborted and <tt>Base#save</tt> will return +false+. If Base#save! is called it will raise a
- # ActiveRecord::RecordInvalid exception. Nothing will be appended to the errors object.
+ # <tt>ActiveRecord::RecordInvalid</tt> exception. Nothing will be appended to the errors object.
#
# == Canceling callbacks
#
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 77e64a22be..6535121075 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -1,7 +1,6 @@
require 'thread'
require 'thread_safe'
require 'monitor'
-require 'set'
module ActiveRecord
# Raised when a connection could not be obtained within the connection
@@ -10,6 +9,12 @@ module ActiveRecord
class ConnectionTimeoutError < ConnectionNotEstablished
end
+ # Raised when a pool was unable to get ahold of all its connections
+ # to perform a "group" action such as +ConnectionPool#disconnect!+
+ # or +ConnectionPool#clear_reloadable_connections!+.
+ class ExclusiveConnectionTimeoutError < ConnectionTimeoutError
+ end
+
module ConnectionAdapters
# Connection pool base class for managing Active Record database
# connections.
@@ -63,6 +68,15 @@ module ActiveRecord
# connection at the end of a thread or a thread dies unexpectedly.
# Regardless of this setting, the Reaper will be invoked before every
# blocking wait. (Default nil, which means don't schedule the Reaper).
+ #
+ #--
+ # Synchronization policy:
+ # * all public methods can be called outside +synchronize+
+ # * access to these i-vars needs to be in +synchronize+:
+ # * @connections
+ # * @now_connecting
+ # * private methods that require being called in a +synchronize+ blocks
+ # are now explicitly documented
class ConnectionPool
# Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
# with which it shares a Monitor. But could be a generic Queue.
@@ -129,17 +143,15 @@ module ActiveRecord
# - ConnectionTimeoutError if +timeout+ is given and no element
# becomes available within +timeout+ seconds,
def poll(timeout = nil)
- synchronize do
- if timeout
- no_wait_poll || wait_poll(timeout)
- else
- no_wait_poll
- end
- end
+ synchronize { internal_poll(timeout) }
end
private
+ def internal_poll(timeout)
+ no_wait_poll || (timeout && wait_poll(timeout))
+ end
+
def synchronize(&block)
@lock.synchronize(&block)
end
@@ -193,6 +205,80 @@ module ActiveRecord
end
end
+ # Adds the ability to turn a basic fair FIFO queue into one
+ # biased to some thread.
+ module BiasableQueue # :nodoc:
+ class BiasedConditionVariable # :nodoc:
+ # semantics of condition variables guarantee that +broadcast+, +broadcast_on_biased+,
+ # +signal+ and +wait+ methods are only called while holding a lock
+ def initialize(lock, other_cond, preferred_thread)
+ @real_cond = lock.new_cond
+ @other_cond = other_cond
+ @preferred_thread = preferred_thread
+ @num_waiting_on_real_cond = 0
+ end
+
+ def broadcast
+ broadcast_on_biased
+ @other_cond.broadcast
+ end
+
+ def broadcast_on_biased
+ @num_waiting_on_real_cond = 0
+ @real_cond.broadcast
+ end
+
+ def signal
+ if @num_waiting_on_real_cond > 0
+ @num_waiting_on_real_cond -= 1
+ @real_cond
+ else
+ @other_cond
+ end.signal
+ end
+
+ def wait(timeout)
+ if Thread.current == @preferred_thread
+ @num_waiting_on_real_cond += 1
+ @real_cond
+ else
+ @other_cond
+ end.wait(timeout)
+ end
+ end
+
+ def with_a_bias_for(thread)
+ previous_cond = nil
+ new_cond = nil
+ synchronize do
+ previous_cond = @cond
+ @cond = new_cond = BiasedConditionVariable.new(@lock, @cond, thread)
+ end
+ yield
+ ensure
+ synchronize do
+ @cond = previous_cond if previous_cond
+ new_cond.broadcast_on_biased if new_cond # wake up any remaining sleepers
+ end
+ end
+ end
+
+ # Connections must be leased while holding the main pool mutex. This is
+ # an internal subclass that also +.leases+ returned connections while
+ # still in queue's critical section (queue synchronizes with the same
+ # +@lock+ as the main pool) so that a returned connection is already
+ # leased and there is no need to re-enter synchronized block.
+ class ConnectionLeasingQueue < Queue # :nodoc:
+ include BiasableQueue
+
+ private
+ def internal_poll(timeout)
+ conn = super
+ conn.lease if conn
+ conn
+ end
+ end
+
# Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
# A reaper instantiated with a nil frequency will never reap the
# connection pool.
@@ -241,56 +327,75 @@ module ActiveRecord
# default max pool size to 5
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
- # The cache of reserved connections mapped to threads
- @reserved_connections = ThreadSafe::Cache.new(:initial_capacity => @size)
+ # The cache of threads mapped to reserved connections, the sole purpose
+ # of the cache is to speed-up +connection+ method, it is not the authoritative
+ # registry of which thread owns which connection, that is tracked by
+ # +connection.owner+ attr on each +connection+ instance.
+ # The invariant works like this: if there is mapping of +thread => conn+,
+ # then that +thread+ does indeed own that +conn+, however an absence of a such
+ # mapping does not mean that the +thread+ doesn't own the said connection, in
+ # that case +conn.owner+ attr should be consulted.
+ # Access and modification of +@thread_cached_conns+ does not require
+ # synchronization.
+ @thread_cached_conns = ThreadSafe::Cache.new(:initial_capacity => @size)
@connections = []
@automatic_reconnect = true
- @available = Queue.new self
+ # Connection pool allows for concurrent (outside the main `synchronize` section)
+ # establishment of new connections. This variable tracks the number of threads
+ # currently in the process of independently establishing connections to the DB.
+ @now_connecting = 0
+
+ # A boolean toggle that allows/disallows new connections.
+ @new_cons_enabled = true
+
+ @available = ConnectionLeasingQueue.new self
end
# Retrieve the connection associated with the current thread, or call
# #checkout to obtain one if necessary.
#
# #connection can be called any number of times; the connection is
- # held in a hash keyed by the thread id.
+ # held in a cache keyed by a thread.
def connection
- # this is correctly done double-checked locking
- # (ThreadSafe::Cache's lookups have volatile semantics)
- @reserved_connections[current_connection_id] || synchronize do
- @reserved_connections[current_connection_id] ||= checkout
- end
+ @thread_cached_conns[connection_cache_key(Thread.current)] ||= checkout
end
# Is there an open connection that is being used for the current thread?
+ #
+ # This method only works for connections that have been abtained through
+ # #connection or #with_connection methods, connections obtained through
+ # #checkout will not be detected by #active_connection?
def active_connection?
- synchronize do
- @reserved_connections.fetch(current_connection_id) {
- return false
- }.in_use?
- end
+ @thread_cached_conns[connection_cache_key(Thread.current)]
end
# Signal that the thread is finished with the current connection.
# #release_connection releases the connection-thread association
# and returns the connection to the pool.
- def release_connection(with_id = current_connection_id)
- synchronize do
- conn = @reserved_connections.delete(with_id)
- checkin conn if conn
+ #
+ # This method only works for connections that have been obtained through
+ # #connection or #with_connection methods, connections obtained through
+ # #checkout will not be automatically released.
+ def release_connection(owner_thread = Thread.current)
+ if conn = @thread_cached_conns.delete(connection_cache_key(owner_thread))
+ checkin conn
end
end
- # If a connection already exists yield it to the block. If no connection
+ # If a connection obtained through #connection or #with_connection methods
+ # already exists yield it to the block. If no such connection
# exists checkout a connection, yield it to the block, and checkin the
# connection when finished.
def with_connection
- connection_id = current_connection_id
- fresh_connection = true unless active_connection?
- yield connection
+ unless conn = @thread_cached_conns[connection_cache_key(Thread.current)]
+ conn = connection
+ fresh_connection = true
+ end
+ yield conn
ensure
- release_connection(connection_id) if fresh_connection
+ release_connection if fresh_connection
end
# Returns true if a connection has already been opened.
@@ -299,32 +404,81 @@ module ActiveRecord
end
# Disconnects all connections in the pool, and clears the pool.
- def disconnect!
- synchronize do
- @reserved_connections.clear
- @connections.each do |conn|
- checkin conn
- conn.disconnect!
+ #
+ # Raises:
+ # - +ExclusiveConnectionTimeoutError+ if unable to gain ownership of all
+ # connections in the pool within a timeout interval (default duration is
+ # +spec.config[:checkout_timeout] * 2+ seconds).
+ def disconnect(raise_on_acquisition_timeout = true)
+ with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
+ synchronize do
+ @connections.each do |conn|
+ checkin conn
+ conn.disconnect!
+ end
+ @connections = []
+ @available.clear
end
- @connections = []
- @available.clear
end
end
- # Clears the cache which maps classes.
- def clear_reloadable_connections!
- synchronize do
- @reserved_connections.clear
- @connections.each do |conn|
- checkin conn
- conn.disconnect! if conn.requires_reloading?
- end
- @connections.delete_if(&:requires_reloading?)
- @available.clear
- @connections.each do |conn|
- @available.add conn
+ # Disconnects all connections in the pool, and clears the pool.
+ #
+ # The pool first tries to gain ownership of all connections, if unable to
+ # do so within a timeout interval (default duration is
+ # +spec.config[:checkout_timeout] * 2+ seconds), the pool is forcefully
+ # disconneted wihout any regard for other connection owning threads.
+ def disconnect!
+ disconnect(false)
+ end
+
+ # Clears the cache which maps classes and re-connects connections that
+ # require reloading.
+ #
+ # Raises:
+ # - +ExclusiveConnectionTimeoutError+ if unable to gain ownership of all
+ # connections in the pool within a timeout interval (default duration is
+ # +spec.config[:checkout_timeout] * 2+ seconds).
+ def clear_reloadable_connections(raise_on_acquisition_timeout = true)
+ num_new_conns_required = 0
+
+ with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
+ synchronize do
+ @connections.each do |conn|
+ checkin conn
+ conn.disconnect! if conn.requires_reloading?
+ end
+ @connections.delete_if(&:requires_reloading?)
+
+ @available.clear
+
+ if @connections.size < @size
+ # because of the pruning done by this method, we might be running
+ # low on connections, while threads stuck in queue are helpless
+ # (not being able to establish new connections for themselves),
+ # see also more detailed explanation in +remove+
+ num_new_conns_required = num_waiting_in_queue - @connections.size
+ end
+
+ @connections.each do |conn|
+ @available.add conn
+ end
end
end
+
+ bulk_make_new_connections(num_new_conns_required) if num_new_conns_required > 0
+ end
+
+ # Clears the cache which maps classes and re-connects connections that
+ # require reloading.
+ #
+ # The pool first tries to gain ownership of all connections, if unable to
+ # do so within a timeout interval (default duration is
+ # +spec.config[:checkout_timeout] * 2+ seconds), the pool forcefully
+ # clears the cache and reloads connections without any regard for other
+ # connection owning threads.
+ def clear_reloadable_connections!
+ clear_reloadable_connections(false)
end
# Check-out a database connection from the pool, indicating that you want
@@ -341,12 +495,8 @@ module ActiveRecord
#
# Raises:
# - ConnectionTimeoutError: no connection can be obtained from the pool.
- def checkout
- synchronize do
- conn = acquire_connection
- conn.lease
- checkout_and_verify(conn)
- end
+ def checkout(checkout_timeout = @checkout_timeout)
+ checkout_and_verify(acquire_connection(checkout_timeout))
end
# Check-in a database connection back into the pool, indicating that you
@@ -356,14 +506,12 @@ module ActiveRecord
# calling +checkout+ on this pool.
def checkin(conn)
synchronize do
- owner = conn.owner
+ remove_connection_from_thread_cache conn
conn.run_callbacks :checkin do
conn.expire
end
- release conn, owner
-
@available.add conn
end
end
@@ -371,14 +519,32 @@ module ActiveRecord
# Remove a connection from the connection pool. The connection will
# remain open and active but will no longer be managed by this pool.
def remove(conn)
+ needs_new_connection = false
+
synchronize do
+ remove_connection_from_thread_cache conn
+
@connections.delete conn
@available.delete conn
- release conn, conn.owner
-
- @available.add checkout_new_connection if @available.any_waiting?
+ # @available.any_waiting? => true means that prior to removing this
+ # conn, the pool was at its max size (@connections.size == @size)
+ # this would mean that any threads stuck waiting in the queue wouldn't
+ # know they could checkout_new_connection, so let's do it for them.
+ # Because condition-wait loop is encapsulated in the Queue class
+ # (that in turn is oblivious to ConnectionPool implementation), threads
+ # that are "stuck" there are helpless, they have no way of creating
+ # new connections and are completely reliant on us feeding available
+ # connections into the Queue.
+ needs_new_connection = @available.any_waiting?
end
+
+ # This is intentionally done outside of the synchronized section as we
+ # would like not to hold the main mutex while checking out new connections,
+ # thus there is some chance that needs_new_connection information is now
+ # stale, we can live with that (bulk_make_new_connections will make
+ # sure not to exceed the pool's @size limit).
+ bulk_make_new_connections(1) if needs_new_connection
end
# Recover lost connections for the pool. A lost connection can occur if
@@ -403,7 +569,118 @@ module ActiveRecord
end
end
+ def num_waiting_in_queue # :nodoc:
+ @available.num_waiting
+ end
+
private
+ #--
+ # this is unfortunately not concurrent
+ def bulk_make_new_connections(num_new_conns_needed)
+ num_new_conns_needed.times do
+ # try_to_checkout_new_connection will not exceed pool's @size limit
+ if new_conn = try_to_checkout_new_connection
+ # make the new_conn available to the starving threads stuck @available Queue
+ checkin(new_conn)
+ end
+ end
+ end
+
+ #--
+ # From the discussion on Github:
+ # https://github.com/rails/rails/pull/14938#commitcomment-6601951
+ # This hook-in method allows for easier monkey-patching fixes needed by
+ # JRuby users that use Fibers.
+ def connection_cache_key(thread)
+ thread
+ end
+
+ # Take control of all existing connections so a "group" action such as
+ # reload/disconnect can be performed safely. It is no longer enough to
+ # wrap it in +synchronize+ because some pool's actions are allowed
+ # to be performed outside of the main +synchronize+ block.
+ def with_exclusively_acquired_all_connections(raise_on_acquisition_timeout = true)
+ with_new_connections_blocked do
+ attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout)
+ yield
+ end
+ end
+
+ def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
+ collected_conns = synchronize do
+ # account for our own connections
+ @connections.select {|conn| conn.owner == Thread.current}
+ end
+
+ newly_checked_out = []
+ timeout_time = Time.now + (@checkout_timeout * 2)
+
+ @available.with_a_bias_for(Thread.current) do
+ while true
+ synchronize do
+ return if collected_conns.size == @connections.size && @now_connecting == 0
+ remaining_timeout = timeout_time - Time.now
+ remaining_timeout = 0 if remaining_timeout < 0
+ conn = checkout_for_exclusive_access(remaining_timeout)
+ collected_conns << conn
+ newly_checked_out << conn
+ end
+ end
+ end
+ rescue ExclusiveConnectionTimeoutError
+ # `raise_on_acquisition_timeout == false` means we are directed to ignore any
+ # timeouts and are expected to just give up: we've obtained as many connections
+ # as possible, note that in a case like that we don't return any of the
+ # `newly_checked_out` connections.
+
+ if raise_on_acquisition_timeout
+ release_newly_checked_out = true
+ raise
+ end
+ rescue Exception # if something else went wrong
+ # this can't be a "naked" rescue, because we have should return conns
+ # even for non-StandardErrors
+ release_newly_checked_out = true
+ raise
+ ensure
+ if release_newly_checked_out && newly_checked_out
+ # releasing only those conns that were checked out in this method, conns
+ # checked outside this method (before it was called) are not for us to release
+ newly_checked_out.each {|conn| checkin(conn)}
+ end
+ end
+
+ #--
+ # Must be called in a synchronize block.
+ def checkout_for_exclusive_access(checkout_timeout)
+ checkout(checkout_timeout)
+ rescue ConnectionTimeoutError
+ # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
+ # rescue block, because doing so would put it outside of synchronize section, without
+ # being in a critical section thread_report might become inaccurate
+ msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds"
+
+ thread_report = []
+ @connections.each do |conn|
+ unless conn.owner == Thread.current
+ thread_report << "#{conn} is owned by #{conn.owner}"
+ end
+ end
+
+ msg << " (#{thread_report.join(', ')})" if thread_report.any?
+
+ raise ExclusiveConnectionTimeoutError, msg
+ end
+
+ def with_new_connections_blocked
+ previous_value = nil
+ synchronize do
+ previous_value, @new_cons_enabled = @new_cons_enabled, false
+ end
+ yield
+ ensure
+ synchronize { @new_cons_enabled = previous_value }
+ end
# Acquire a connection by one of 1) immediately removing one
# from the queue of available connections, 2) creating a new
@@ -412,24 +689,31 @@ module ActiveRecord
#
# Raises:
# - ConnectionTimeoutError if a connection could not be acquired
- def acquire_connection
- if conn = @available.poll
+ #
+ #--
+ # Implementation detail: the connection returned by +acquire_connection+
+ # will already be "+connection.lease+ -ed" to the current thread.
+ def acquire_connection(checkout_timeout)
+ # NOTE: we rely on `@available.poll` and `try_to_checkout_new_connection` to
+ # `conn.lease` the returned connection (and to do this in a `synchronized`
+ # section), this is not the cleanest implementation, as ideally we would
+ # `synchronize { conn.lease }` in this method, but by leaving it to `@available.poll`
+ # and `try_to_checkout_new_connection` we can piggyback on `synchronize` sections
+ # of the said methods and avoid an additional `synchronize` overhead.
+ if conn = @available.poll || try_to_checkout_new_connection
conn
- elsif @connections.size < @size
- checkout_new_connection
else
reap
- @available.poll(@checkout_timeout)
+ @available.poll(checkout_timeout)
end
end
- def release(conn, owner)
- thread_id = owner.object_id
-
- if @reserved_connections[thread_id] == conn
- @reserved_connections.delete thread_id
- end
+ #--
+ # if owner_thread param is omitted, this must be called in synchronize block
+ def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
+ @thread_cached_conns.delete_pair(connection_cache_key(owner_thread), conn)
end
+ alias_method :release, :remove_connection_from_thread_cache
def new_connection
Base.send(spec.adapter_method, spec.config).tap do |conn|
@@ -437,17 +721,46 @@ module ActiveRecord
end
end
- def current_connection_id #:nodoc:
- Base.connection_id ||= Thread.current.object_id
+ # If the pool is not at a +@size+ limit, establish new connection. Connecting
+ # to the DB is done outside main synchronized section.
+ #--
+ # Implementation constraint: a newly established connection returned by this
+ # method must be in the +.leased+ state.
+ def try_to_checkout_new_connection
+ # first in synchronized section check if establishing new conns is allowed
+ # and increment @now_connecting, to prevent overstepping this pool's @size
+ # constraint
+ do_checkout = synchronize do
+ if @new_cons_enabled && (@connections.size + @now_connecting) < @size
+ @now_connecting += 1
+ end
+ end
+ if do_checkout
+ begin
+ # if successfully incremented @now_connecting establish new connection
+ # outside of synchronized section
+ conn = checkout_new_connection
+ ensure
+ synchronize do
+ if conn
+ adopt_connection(conn)
+ # returned conn needs to be already leased
+ conn.lease
+ end
+ @now_connecting -= 1
+ end
+ end
+ end
+ end
+
+ def adopt_connection(conn)
+ conn.pool = self
+ @connections << conn
end
def checkout_new_connection
raise ConnectionNotEstablished unless @automatic_reconnect
-
- c = new_connection
- c.pool = self
- @connections << c
- c
+ new_connection
end
def checkout_and_verify(c)
@@ -620,7 +933,9 @@ module ActiveRecord
# A connection was established in an ancestor process that must have
# subsequently forked. We can't reuse the connection, but we can copy
# the specification and establish a new connection with it.
- establish_connection owner, ancestor_pool.spec
+ establish_connection(owner, ancestor_pool.spec).tap do |pool|
+ pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
+ end
else
owner_to_pool[owner.name] = nil
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 431fe25501..38dd9578fe 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -189,7 +189,7 @@ module ActiveRecord
# You should consult the documentation for your database to understand the
# semantics of these different levels:
#
- # * http://www.postgresql.org/docs/9.1/static/transaction-iso.html
+ # * http://www.postgresql.org/docs/current/static/transaction-iso.html
# * https://dev.mysql.com/doc/refman/5.6/en/set-transaction.html
#
# An <tt>ActiveRecord::TransactionIsolationError</tt> will be raised if:
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 0ccf0c498b..158b773e11 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -1,8 +1,3 @@
-require 'date'
-require 'set'
-require 'bigdecimal'
-require 'bigdecimal/util'
-
module ActiveRecord
module ConnectionAdapters #:nodoc:
# Abstract representation of an index definition on a table. Instances of
@@ -403,17 +398,12 @@ module ActiveRecord
column(:updated_at, :datetime, options)
end
- # Adds a reference. Optionally adds a +type+ column, if the
- # +:polymorphic+ option is provided. +references+ and +belongs_to+
- # are interchangeable. The reference column will be an +integer+ by default,
- # the +:type+ option can be used to specify a different type. A foreign
- # key will be created if the +:foreign_key+ option is passed.
+ # Adds a reference.
#
# t.references(:user)
- # t.references(:user, type: "string")
- # t.belongs_to(:supplier, polymorphic: true)
+ # t.belongs_to(:supplier, foreign_key: true)
#
- # See SchemaStatements#add_reference
+ # See SchemaStatements#add_reference for details of the options you can use.
def references(*args, **options)
args.each do |col|
ReferenceDefinition.new(col, **options).add_to(self)
@@ -647,15 +637,12 @@ module ActiveRecord
@base.rename_column(name, column_name, new_column_name)
end
- # Adds a reference. Optionally adds a +type+ column, if
- # <tt>:polymorphic</tt> option is provided.
+ # Adds a reference.
#
# t.references(:user)
- # t.references(:user, type: "string")
- # t.belongs_to(:supplier, polymorphic: true)
# t.belongs_to(:supplier, foreign_key: true)
#
- # See SchemaStatements#add_reference
+ # See SchemaStatements#add_reference for details of the options you can use.
def references(*args)
options = args.extract_options!
args.each do |ref_name|
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index deb014ad46..b944a8631c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -27,10 +27,17 @@ module ActiveRecord
spec[:type] = schema_type(column)
spec[:null] = 'false' unless column.null
- limit = column.limit || native_database_types[column.type][:limit]
- spec[:limit] = limit.inspect if limit
- spec[:precision] = column.precision.inspect if column.precision
- spec[:scale] = column.scale.inspect if column.scale
+ if limit = schema_limit(column)
+ spec[:limit] = limit
+ end
+
+ if precision = schema_precision(column)
+ spec[:precision] = precision
+ end
+
+ if scale = schema_scale(column)
+ spec[:scale] = scale
+ end
default = schema_default(column) if column.has_default?
spec[:default] = default unless default.nil?
@@ -53,6 +60,19 @@ module ActiveRecord
column.type.to_s
end
+ def schema_limit(column)
+ limit = column.limit || native_database_types[column.type][:limit]
+ limit.inspect if limit
+ end
+
+ def schema_precision(column)
+ column.precision.inspect if column.precision
+ end
+
+ def schema_scale(column)
+ column.scale.inspect if column.scale
+ end
+
def schema_default(column)
type = lookup_cast_type_from_column(column)
default = type.deserialize(column.default)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 9004d86b04..dd7dcb9cdd 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -649,11 +649,21 @@ module ActiveRecord
indexes(table_name).detect { |i| i.name == index_name }
end
- # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
- # The reference column is an +integer+ by default, the <tt>:type</tt> option can be used to specify
- # a different type.
+ # Adds a reference. The reference column is an integer by default,
+ # the <tt>:type</tt> option can be used to specify a different type.
+ # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
# <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
#
+ # The +options+ hash can include the following keys:
+ # [<tt>:type</tt>]
+ # The reference column type. Defaults to +:integer+.
+ # [<tt>:index</tt>]
+ # Add an appropriate index. Defaults to false.
+ # [<tt>:foreign_key</tt>]
+ # Add an appropriate foreign key. Defaults to false.
+ # [<tt>:polymorphic</tt>]
+ # Whether an additional +_type+ column should be added. Defaults to false.
+ #
# ====== Create a user_id integer column
#
# add_reference(:products, :user)
@@ -662,10 +672,6 @@ module ActiveRecord
#
# add_reference(:products, :user, type: :string)
#
- # ====== Create a supplier_id and supplier_type columns
- #
- # add_belongs_to(:products, :supplier, polymorphic: true)
- #
# ====== Create supplier_id, supplier_type columns and appropriate index
#
# add_reference(:products, :supplier, polymorphic: true, index: true)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 0705c22a8c..6d3a21a3dc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -1,13 +1,9 @@
-require 'date'
-require 'bigdecimal'
-require 'bigdecimal/util'
require 'active_record/type'
require 'active_support/core_ext/benchmark'
require 'active_record/connection_adapters/schema_cache'
require 'active_record/connection_adapters/sql_type_metadata'
require 'active_record/connection_adapters/abstract/schema_dumper'
require 'active_record/connection_adapters/abstract/schema_creation'
-require 'monitor'
require 'arel/collectors/bind'
require 'arel/collectors/sql_string'
@@ -70,7 +66,6 @@ module ActiveRecord
include DatabaseLimits
include QueryCache
include ActiveSupport::Callbacks
- include MonitorMixin
include ColumnDumper
SIMPLE_INT = /\A\d+\z/
@@ -141,12 +136,20 @@ module ActiveRecord
SchemaCreation.new self
end
+ # this method must only be called while holding connection pool's mutex
def lease
- synchronize do
- unless in_use?
- @owner = Thread.current
+ if in_use?
+ msg = 'Cannot lease connection, '
+ if @owner == Thread.current
+ msg << 'it is already leased by the current thread.'
+ else
+ msg << "it is already in use by a different thread: #{@owner}. " <<
+ "Current thread: #{Thread.current}."
end
+ raise ActiveRecordError, msg
end
+
+ @owner = Thread.current
end
def schema_cache=(cache)
@@ -154,6 +157,7 @@ module ActiveRecord
@schema_cache = cache
end
+ # this method must only be called while holding connection pool's mutex
def expire
@owner = nil
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 8c2b4ccac4..00e3d2965b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -1,4 +1,3 @@
-require 'arel/visitors/bind_visitor'
require 'active_support/core_ext/string/strip'
module ActiveRecord
@@ -122,11 +121,14 @@ module ActiveRecord
spec
end
- def prepare_column_options(column)
- spec = super
- spec.delete(:precision) if /time/ === column.sql_type && column.precision == 0
- spec.delete(:limit) if :boolean === column.type
- spec
+ private
+
+ def schema_limit(column)
+ super unless column.type == :boolean
+ end
+
+ def schema_precision(column)
+ super unless /time/ === column.sql_type && column.precision == 0
end
def schema_collation(column)
@@ -136,7 +138,8 @@ module ActiveRecord
column.collation.inspect if column.collation != @collation_cache[table_name]
end
end
- private :schema_collation
+
+ public
class Column < ConnectionAdapters::Column # :nodoc:
delegate :strict, :extra, to: :sql_type_metadata, allow_nil: true
@@ -697,29 +700,11 @@ module ActiveRecord
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
case type.to_s
when 'binary'
- case limit
- when 0..0xfff; "varbinary(#{limit})"
- when nil; "blob"
- when 0x1000..0xffffffff; "blob(#{limit})"
- else raise(ActiveRecordError, "No binary type has character length #{limit}")
- end
+ binary_to_sql(limit)
when 'integer'
- case limit
- when 1; 'tinyint'
- when 2; 'smallint'
- when 3; 'mediumint'
- when nil, 4, 11; 'int(11)' # compatibility with MySQL default
- when 5..8; 'bigint'
- else raise(ActiveRecordError, "No integer type has byte size #{limit}")
- end
+ integer_to_sql(limit)
when 'text'
- case limit
- when 0..0xff; 'tinytext'
- when nil, 0x100..0xffff; 'text'
- when 0x10000..0xffffff; 'mediumtext'
- when 0x1000000..0xffffffff; 'longtext'
- else raise(ActiveRecordError, "No text type has character length #{limit}")
- end
+ text_to_sql(limit)
else
super
end
@@ -974,10 +959,12 @@ module ActiveRecord
wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout)
+ defaults = [':default', :default].to_set
+
# Make MySQL reject illegal values rather than truncating or blanking them, see
# http://dev.mysql.com/doc/refman/5.6/en/sql-mode.html#sqlmode_strict_all_tables
# If the user has provided another value for sql_mode, don't replace it.
- unless variables.has_key?('sql_mode')
+ unless variables.has_key?('sql_mode') || defaults.include?(@config[:strict])
variables['sql_mode'] = strict_mode? ? 'STRICT_ALL_TABLES' : ''
end
@@ -992,7 +979,7 @@ module ActiveRecord
# Gather up all of the SET variables...
variable_assignments = variables.map do |k, v|
- if v == ':default' || v == :default
+ if defaults.include?(v)
"@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
elsif !v.nil?
"@@SESSION.#{k} = #{quote(v)}"
@@ -1017,6 +1004,36 @@ module ActiveRecord
TableDefinition.new(native_database_types, name, temporary, options, as)
end
+ def binary_to_sql(limit) # :nodoc:
+ case limit
+ when 0..0xfff; "varbinary(#{limit})"
+ when nil; "blob"
+ when 0x1000..0xffffffff; "blob(#{limit})"
+ else raise(ActiveRecordError, "No binary type has character length #{limit}")
+ end
+ end
+
+ def integer_to_sql(limit) # :nodoc:
+ case limit
+ when 1; 'tinyint'
+ when 2; 'smallint'
+ when 3; 'mediumint'
+ when nil, 4, 11; 'int(11)' # compatibility with MySQL default
+ when 5..8; 'bigint'
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}")
+ end
+ end
+
+ def text_to_sql(limit) # :nodoc:
+ case limit
+ when 0..0xff; 'tinytext'
+ when nil, 0x100..0xffff; 'text'
+ when 0x10000..0xffffff; 'mediumtext'
+ when 0x1000000..0xffffffff; 'longtext'
+ else raise(ActiveRecordError, "No text type has character length #{limit}")
+ end
+ end
+
class MysqlString < Type::String # :nodoc:
def serialize(value)
case value
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 18febf66b4..2ae462d773 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -71,34 +71,10 @@ module ActiveRecord
ADAPTER_NAME = 'MySQL'.freeze
class StatementPool < ConnectionAdapters::StatementPool
- def initialize(connection, max = 1000)
- super
- @cache = Hash.new { |h,pid| h[pid] = {} }
- end
-
- def each(&block); cache.each(&block); end
- def key?(key); cache.key?(key); end
- def [](key); cache[key]; end
- def length; cache.length; end
- def delete(key); cache.delete(key); end
-
- def []=(sql, key)
- while @max <= cache.size
- cache.shift.last[:stmt].close
- end
- cache[sql] = key
- end
-
- def clear
- cache.each_value do |hash|
- hash[:stmt].close
- end
- cache.clear
- end
-
private
- def cache
- @cache[Process.pid]
+
+ def dealloc(stmt)
+ stmt[:stmt].close
end
end
@@ -416,8 +392,11 @@ module ActiveRecord
# place when an error occurs. To support older MySQL versions, we
# need to close the statement and delete the statement from the
# cache.
- stmt.close
- @statements.delete sql
+ if binds.empty?
+ stmt.close
+ else
+ @statements.delete sql
+ end
raise e
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
index be13ead120..bfa03fa136 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
@@ -8,7 +8,8 @@ module ActiveRecord
def serial?
return unless default_function
- %r{\Anextval\('(?<table_name>.+)_#{name}_seq'::regclass\)\z} === default_function
+ table_name = @table_name || '(?<table_name>.+)'
+ %r{\Anextval\('"?#{table_name}_#{name}_seq"?'::regclass\)\z} === default_function
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
index 92349e2f9b..68752cdd80 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -12,6 +12,7 @@ require 'active_record/connection_adapters/postgresql/oid/json'
require 'active_record/connection_adapters/postgresql/oid/jsonb'
require 'active_record/connection_adapters/postgresql/oid/money'
require 'active_record/connection_adapters/postgresql/oid/point'
+require 'active_record/connection_adapters/postgresql/oid/rails_5_1_point'
require 'active_record/connection_adapters/postgresql/oid/range'
require 'active_record/connection_adapters/postgresql/oid/specialized_string'
require 'active_record/connection_adapters/postgresql/oid/uuid'
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
index 3de794f797..25961a9869 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -45,6 +45,11 @@ module ActiveRecord
delimiter == other.delimiter
end
+ def type_cast_for_schema(value)
+ return super unless value.is_a?(::Array)
+ "[" + value.map { |v| subtype.type_cast_for_schema(v) }.join(", ") + "]"
+ end
+
private
def type_cast_array(value, method)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb
index afc9383f91..87391b5dc7 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb
@@ -9,7 +9,7 @@ module ActiveRecord
def changed_in_place?(raw_old_value, new_value)
# Postgres does not preserve insignificant whitespaces when
- # roundtripping jsonb columns. This causes some false positives for
+ # round-tripping jsonb columns. This causes some false positives for
# the comparison here. Therefore, we need to parse and re-dump the
# raw value here to ensure the insignificant whitespaces are
# consistent with our encoder's output.
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb
new file mode 100644
index 0000000000..7427a25ad5
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb
@@ -0,0 +1,50 @@
+module ActiveRecord
+ Point = Struct.new(:x, :y)
+
+ module ConnectionAdapters
+ module PostgreSQL
+ module OID # :nodoc:
+ class Rails51Point < Type::Value # :nodoc:
+ include Type::Helpers::Mutable
+
+ def type
+ :point
+ end
+
+ def cast(value)
+ case value
+ when ::String
+ if value[0] == '(' && value[-1] == ')'
+ value = value[1...-1]
+ end
+ x, y = value.split(",")
+ build_point(x, y)
+ when ::Array
+ build_point(*value)
+ else
+ value
+ end
+ end
+
+ def serialize(value)
+ if value.is_a?(ActiveRecord::Point)
+ "(#{number_for_point(value.x)},#{number_for_point(value.y)})"
+ else
+ super
+ end
+ end
+
+ private
+
+ def number_for_point(number)
+ number.to_s.gsub(/\.0$/, '')
+ end
+
+ def build_point(x, y)
+ ActiveRecord::Point.new(Float(x), Float(y))
+ end
+ end
+ end
+ end
+ end
+end
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 686671b007..595c635fc0 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -70,11 +70,7 @@ module ActiveRecord
# Returns the list of all tables in the schema search path or a specified schema.
def tables(name = nil)
- select_values(<<-SQL, 'SCHEMA')
- SELECT tablename
- FROM pg_tables
- WHERE schemaname = ANY (current_schemas(false))
- SQL
+ select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", 'SCHEMA')
end
# Returns true if table exists.
@@ -100,11 +96,7 @@ module ActiveRecord
# Returns true if schema exists.
def schema_exists?(name)
- select_value(<<-SQL, 'SCHEMA').to_i > 0
- SELECT COUNT(*)
- FROM pg_namespace
- WHERE nspname = '#{name}'
- SQL
+ select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", 'SCHEMA').to_i > 0
end
# Verifies existence of an index with a given name.
@@ -192,24 +184,17 @@ module ActiveRecord
# Returns the current database encoding format.
def encoding
- select_value(<<-end_sql, 'SCHEMA')
- SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
- WHERE pg_database.datname LIKE '#{current_database}'
- end_sql
+ select_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
end
# Returns the current database collation.
def collation
- select_value(<<-end_sql, 'SCHEMA')
- SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
- end_sql
+ select_value("SELECT datcollate FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
end
# Returns the current database ctype.
def ctype
- select_value(<<-end_sql, 'SCHEMA')
- SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
- end_sql
+ select_value("SELECT datctype FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
end
# Returns an array of schema names.
@@ -398,9 +383,7 @@ module ActiveRecord
rename_table_indexes(table_name, new_name)
end
- # Adds a new column to the named table.
- # See TableDefinition#column for details of the options you can use.
- def add_column(table_name, column_name, type, options = {})
+ def add_column(table_name, column_name, type, options = {}) #:nodoc:
clear_cache!
super
end
@@ -452,7 +435,7 @@ module ActiveRecord
end
# Renames a column in a table.
- def rename_column(table_name, column_name, new_column_name)
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
clear_cache!
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
rename_column_indexes(table_name, column_name, new_column_name)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 2b33a5b9cb..2c43c46a3d 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -14,8 +14,6 @@ require "active_record/connection_adapters/postgresql/type_metadata"
require "active_record/connection_adapters/postgresql/utils"
require "active_record/connection_adapters/statement_pool"
-require 'arel/visitors/bind_visitor'
-
require 'ipaddr'
module ActiveRecord
@@ -68,11 +66,11 @@ module ActiveRecord
# defaults to true.
#
# Any further options are used as connection parameters to libpq. See
- # http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
+ # http://www.postgresql.org/docs/current/static/libpq-connect.html for the
# list of parameters.
#
# In addition, default connection parameters of libpq can be set per environment variables.
- # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
+ # See http://www.postgresql.org/docs/current/static/libpq-envars.html .
class PostgreSQLAdapter < AbstractAdapter
ADAPTER_NAME = 'PostgreSQL'.freeze
@@ -211,44 +209,18 @@ module ActiveRecord
def initialize(connection, max)
super
@counter = 0
- @cache = Hash.new { |h,pid| h[pid] = {} }
end
- def each(&block); cache.each(&block); end
- def key?(key); cache.key?(key); end
- def [](key); cache[key]; end
- def length; cache.length; end
-
def next_key
"a#{@counter + 1}"
end
def []=(sql, key)
- while @max <= cache.size
- dealloc(cache.shift.last)
- end
- @counter += 1
- cache[sql] = key
- end
-
- def clear
- cache.each_value do |stmt_key|
- dealloc stmt_key
- end
- cache.clear
- end
-
- def delete(sql_key)
- dealloc cache[sql_key]
- cache.delete sql_key
+ super.tap { @counter += 1 }
end
private
- def cache
- @cache[Process.pid]
- end
-
def dealloc(key)
@connection.query "DEALLOCATE #{key}" if connection_active?
end
@@ -452,7 +424,7 @@ module ActiveRecord
@connection.server_version
end
- # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
+ # See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
FOREIGN_KEY_VIOLATION = "23503"
UNIQUE_VIOLATION = "23505"
@@ -726,7 +698,7 @@ module ActiveRecord
end
# SET statements from :variables config hash
- # http://www.postgresql.org/docs/8.3/static/sql-set.html
+ # http://www.postgresql.org/docs/current/static/sql-set.html
variables = @config[:variables] || {}
variables.map do |k, v|
if v == ':default' || v == :default
@@ -866,6 +838,8 @@ module ActiveRecord
ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
+ ActiveRecord::Type.register(:legacy_point, OID::Point, adapter: :postgresql)
+ ActiveRecord::Type.register(:rails_5_1_point, OID::Rails51Point, adapter: :postgresql)
ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_creation.rb
new file mode 100644
index 0000000000..fe1dcbd710
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_creation.rb
@@ -0,0 +1,15 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module SQLite3
+ class SchemaCreation < AbstractAdapter::SchemaCreation
+ private
+ def add_column_options!(sql, options)
+ if options[:collation]
+ sql << " COLLATE \"#{options[:collation]}\""
+ end
+ super
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 3186769510..87129c42cf 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -1,6 +1,6 @@
require 'active_record/connection_adapters/abstract_adapter'
require 'active_record/connection_adapters/statement_pool'
-require 'arel/visitors/bind_visitor'
+require 'active_record/connection_adapters/sqlite3/schema_creation'
gem 'sqlite3', '~> 1.3.6'
require 'sqlite3'
@@ -78,40 +78,17 @@ module ActiveRecord
end
class StatementPool < ConnectionAdapters::StatementPool
- def initialize(connection, max)
- super
- @cache = Hash.new { |h,pid| h[pid] = {} }
- end
-
- def each(&block); cache.each(&block); end
- def key?(key); cache.key?(key); end
- def [](key); cache[key]; end
- def length; cache.length; end
-
- def []=(sql, key)
- while @max <= cache.size
- dealloc(cache.shift.last[:stmt])
- end
- cache[sql] = key
- end
-
- def clear
- cache.each_value do |hash|
- dealloc hash[:stmt]
- end
- cache.clear
- end
-
private
- def cache
- @cache[$$]
- end
def dealloc(stmt)
- stmt.close unless stmt.closed?
+ stmt[:stmt].close unless stmt[:stmt].closed?
end
end
+ def schema_creation # :nodoc:
+ SQLite3::SchemaCreation.new self
+ end
+
def initialize(connection, logger, connection_options, config)
super(connection, logger)
@@ -372,9 +349,10 @@ module ActiveRecord
field["dflt_value"] = $1.gsub('""', '"')
end
+ collation = field['collation']
sql_type = field['type']
type_metadata = fetch_type_metadata(sql_type)
- new_column(field['name'], field['dflt_value'], type_metadata, field['notnull'].to_i == 0)
+ new_column(field['name'], field['dflt_value'], type_metadata, field['notnull'].to_i == 0, nil, collation)
end
end
@@ -469,6 +447,7 @@ module ActiveRecord
self.null = options[:null] if options.include?(:null)
self.precision = options[:precision] if options.include?(:precision)
self.scale = options[:scale] if options.include?(:scale)
+ self.collation = options[:collation] if options.include?(:collation)
end
end
end
@@ -482,9 +461,9 @@ module ActiveRecord
protected
def table_structure(table_name)
- structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA').to_hash
+ structure = exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", 'SCHEMA')
raise(ActiveRecord::StatementInvalid, "Could not find table '#{table_name}'") if structure.empty?
- structure
+ table_structure_with_collation(table_name, structure)
end
def alter_table(table_name, options = {}) #:nodoc:
@@ -519,7 +498,7 @@ module ActiveRecord
@definition.column(column_name, column.type,
:limit => column.limit, :default => column.default,
:precision => column.precision, :scale => column.scale,
- :null => column.null)
+ :null => column.null, collation: column.collation)
end
yield @definition if block_given?
end
@@ -581,6 +560,46 @@ module ActiveRecord
super
end
end
+
+ private
+ COLLATE_REGEX = /.*\"(\w+)\".*collate\s+\"(\w+)\".*/i.freeze
+
+ def table_structure_with_collation(table_name, basic_structure)
+ collation_hash = {}
+ sql = "SELECT sql FROM
+ (SELECT * FROM sqlite_master UNION ALL
+ SELECT * FROM sqlite_temp_master)
+ WHERE type='table' and name='#{ table_name }' \;"
+
+ # Result will have following sample string
+ # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ # "password_digest" varchar COLLATE "NOCASE");
+ result = exec_query(sql, 'SCHEMA').first
+
+ if result
+ # Splitting with left parantheses and picking up last will return all
+ # columns separated with comma(,).
+ columns_string = result["sql"].split('(').last
+
+ columns_string.split(',').each do |column_string|
+ # This regex will match the column name and collation type and will save
+ # the value in $1 and $2 respectively.
+ collation_hash[$1] = $2 if (COLLATE_REGEX =~ column_string)
+ end
+
+ basic_structure.map! do |column|
+ column_name = column['name']
+
+ if collation_hash.has_key? column_name
+ column['collation'] = collation_hash[column_name]
+ end
+
+ column
+ end
+ else
+ basic_structure.to_hash
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/statement_pool.rb b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
index c6b1bc8b5b..82e9ef3d3d 100644
--- a/activerecord/lib/active_record/connection_adapters/statement_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/statement_pool.rb
@@ -4,35 +4,53 @@ module ActiveRecord
include Enumerable
def initialize(connection, max = 1000)
+ @cache = Hash.new { |h,pid| h[pid] = {} }
@connection = connection
@max = max
end
- def each
- raise NotImplementedError
+ def each(&block)
+ cache.each(&block)
end
def key?(key)
- raise NotImplementedError
+ cache.key?(key)
end
def [](key)
- raise NotImplementedError
+ cache[key]
end
def length
- raise NotImplementedError
+ cache.length
end
- def []=(sql, key)
- raise NotImplementedError
+ def []=(sql, stmt)
+ while @max <= cache.size
+ dealloc(cache.shift.last)
+ end
+ cache[sql] = stmt
end
def clear
- raise NotImplementedError
+ cache.each_value do |stmt|
+ dealloc stmt
+ end
+ cache.clear
end
def delete(key)
+ dealloc cache[key]
+ cache.delete(key)
+ end
+
+ private
+
+ def cache
+ @cache[Process.pid]
+ end
+
+ def dealloc(stmt)
raise NotImplementedError
end
end
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index 24f5849e45..d6b661ff76 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -88,7 +88,7 @@ module ActiveRecord
end
def connection_id
- ActiveRecord::RuntimeRegistry.connection_id
+ ActiveRecord::RuntimeRegistry.connection_id ||= Thread.current.object_id
end
def connection_id=(connection_id)
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 2c1771dd6c..738fab2bd9 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -615,7 +615,6 @@ module ActiveRecord
# a list of rows to insert to that table.
def table_rows
now = config.default_timezone == :utc ? Time.now.utc : Time.now
- now = now.to_s(:db)
# allow a standard key to be used for doing defaults in YAML
fixtures.delete('DEFAULTS')
@@ -644,6 +643,11 @@ module ActiveRecord
row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type)
end
+ # Resolve enums
+ model_class.defined_enums.each do |name, values|
+ row[name] = values.fetch(row[name], row[name])
+ end
+
# If STI is used, find the correct subclass for association reflection
reflection_class =
if row.include?(inheritance_column_name)
@@ -664,7 +668,7 @@ module ActiveRecord
row[association.foreign_type] = $1
end
- fk_type = association.active_record.type_for_attribute(fk_name).type
+ fk_type = reflection_class.type_for_attribute(fk_name).type
row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
end
when :has_many
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 24098f72dc..e613d157aa 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -198,14 +198,16 @@ module ActiveRecord
def subclass_from_attributes(attrs)
subclass_name = attrs.with_indifferent_access[inheritance_column]
- if subclass_name.present? && subclass_name != self.name
- subclass = subclass_name.safe_constantize
+ if subclass_name.present?
+ subclass = find_sti_class(subclass_name)
- unless descendants.include?(subclass)
- raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}")
- end
+ if subclass.name != self.name
+ unless descendants.include?(subclass)
+ raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}")
+ end
- subclass
+ subclass
+ end
end
end
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index a83b90a95f..192a456846 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -275,21 +275,6 @@ module ActiveRecord
# The phrase "Updating salaries..." would then be printed, along with the
# benchmark for the block when the block completes.
#
- # == About the schema_migrations table
- #
- # Rails versions 2.0 and prior used to create a table called
- # <tt>schema_info</tt> when using migrations. This table contained the
- # version of the schema as of the last applied migration.
- #
- # Starting with Rails 2.1, the <tt>schema_info</tt> table is
- # (automatically) replaced by the <tt>schema_migrations</tt> table, which
- # contains the version numbers of all the migrations applied.
- #
- # As a result, it is now possible to add migration files that are numbered
- # lower than the current schema version: when migrating up, those
- # never-applied "interleaved" migrations will be automatically applied, and
- # when migrating down, never-applied "interleaved" migrations will be skipped.
- #
# == Timestamped Migrations
#
# By default, Rails generates migrations that look like:
@@ -307,9 +292,8 @@ module ActiveRecord
#
# == Reversible Migrations
#
- # Starting with Rails 3.1, you will be able to define reversible migrations.
# Reversible migrations are migrations that know how to go +down+ for you.
- # You simply supply the +up+ logic, and the Migration system will figure out
+ # You simply supply the +up+ logic, and the Migration system figures out
# how to execute the down commands for you.
#
# To define a reversible migration, define the +change+ method in your
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 3674f672cb..5a6f42ba09 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -310,6 +310,7 @@ module ActiveRecord
def load_schema!
@columns_hash = connection.schema_cache.columns_hash(table_name)
@columns_hash.each do |name, column|
+ warn_if_deprecated_type(column)
define_attribute(
name,
connection.lookup_cast_type_from_column(column),
@@ -356,6 +357,28 @@ module ActiveRecord
base.table_name
end
end
+
+ def warn_if_deprecated_type(column)
+ return if attributes_to_define_after_schema_loads.key?(column.name)
+ if column.respond_to?(:oid) && column.sql_type.start_with?("point")
+ if column.array?
+ array_arguments = ", array: true"
+ else
+ array_arguments = ""
+ end
+ ActiveSupport::Deprecation.warn(<<-WARNING.strip_heredoc)
+ The behavior of the `:point` type will be changing in Rails 5.1 to
+ return a `Point` object, rather than an `Array`. If you'd like to
+ keep the old behavior, you can add this line to #{self.name}:
+
+ attribute :#{column.name}, :legacy_point#{array_arguments}
+
+ If you'd like the new behavior today, you can add this line:
+
+ attribute :#{column.name}, :rails_5_1_point#{array_arguments}
+ WARNING
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 90e37e80d2..c942d0e265 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -166,6 +166,11 @@ module ActiveRecord
# member.posts.first.title # => '[UPDATED] An, as of yet, undisclosed awesome Ruby documentation browser!'
# member.posts.second.title # => '[UPDATED] other post'
#
+ # However, the above applies if the parent model is being updated as well.
+ # For example, If you wanted to create a +member+ named _joe_ and wanted to
+ # update the +posts+ at the same time, that would give an
+ # ActiveRecord::RecordNotFound error.
+ #
# By default the associated records are protected from being destroyed. If
# you want to destroy any of the associated records through the attributes
# hash, you have to enable it first using the <tt>:allow_destroy</tt>
@@ -208,7 +213,7 @@ module ActiveRecord
#
# Passing attributes for an associated collection in the form of a hash
# of hashes can be used with hashes generated from HTTP/HTML parameters,
- # where there maybe no natural way to submit an array of hashes.
+ # where there may be no natural way to submit an array of hashes.
#
# === Saving
#
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 466175690e..0a6e4ac0bd 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -211,8 +211,7 @@ module ActiveRecord
def becomes(klass)
became = klass.new
became.instance_variable_set("@attributes", @attributes)
- changed_attributes = @changed_attributes if defined?(@changed_attributes)
- became.instance_variable_set("@changed_attributes", changed_attributes || {})
+ became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
became.instance_variable_set("@new_record", new_record?)
became.instance_variable_set("@destroyed", destroyed?)
became.instance_variable_set("@errors", errors)
@@ -382,7 +381,7 @@ module ActiveRecord
# # => #<Account id: 1, email: 'account@example.com'>
#
# Attributes are reloaded from the database, and caches busted, in
- # particular the associations cache.
+ # particular the associations cache and the QueryCache.
#
# If the record no longer exists in the database <tt>ActiveRecord::RecordNotFound</tt>
# is raised. Otherwise, in addition to the in-place modification the method
@@ -418,6 +417,8 @@ module ActiveRecord
# end
#
def reload(options = nil)
+ self.class.connection.clear_query_cache
+
fresh_object =
if options && options[:lock]
self.class.unscoped { self.class.lock(options[:lock]).find(id) }
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 5af64b717a..da6b8447d3 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -156,8 +156,8 @@ end_warning
ActiveSupport.on_load(:active_record) do
ActionDispatch::Reloader.send(hook) do
if ActiveRecord::Base.connected?
- ActiveRecord::Base.clear_reloadable_connections!
ActiveRecord::Base.clear_cache!
+ ActiveRecord::Base.clear_reloadable_connections!
end
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 1b0ae2c942..5360db6a19 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -69,9 +69,11 @@ module ActiveRecord
def reflections
ref = {}
_reflections.each do |name, reflection|
- parent_name, parent_reflection = reflection.parent_reflection
- if parent_name
- ref[parent_name] = parent_reflection
+ parent_reflection = reflection.parent_reflection
+
+ if parent_reflection
+ parent_name = parent_reflection.name
+ ref[parent_name.to_s] = parent_reflection
else
ref[name] = reflection
end
@@ -204,7 +206,7 @@ module ActiveRecord
def autosave=(autosave)
@automatic_inverse_of = false
@options[:autosave] = autosave
- _, parent_reflection = self.parent_reflection
+ parent_reflection = self.parent_reflection
if parent_reflection
parent_reflection.autosave = autosave
end
@@ -272,7 +274,7 @@ module ActiveRecord
end
attr_reader :type, :foreign_type
- attr_accessor :parent_reflection # [:name, Reflection]
+ attr_accessor :parent_reflection # Reflection
def initialize(name, scope, options, active_record)
super
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 85648a7f8f..7d37313058 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -9,7 +9,7 @@ module ActiveRecord
:extending, :unscope]
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
- :reverse_order, :distinct, :create_with, :uniq]
+ :reverse_order, :distinct, :create_with]
CLAUSE_METHODS = [:where, :having, :from]
INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
@@ -618,6 +618,7 @@ module ActiveRecord
def uniq_value
distinct_value
end
+ deprecate uniq_value: :distinct_value
# Compares two relations for equality.
def ==(other)
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 402b317d9c..7a28a98721 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -161,6 +161,10 @@ module ActiveRecord
end
end
+ if loaded? && (column_names - @klass.column_names).empty?
+ return @records.pluck(*column_names)
+ end
+
if has_include?(column_names.first)
construct_relation_for_association_calculations.pluck(*column_names)
else
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index d4a8823cfe..86f2c30168 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -39,7 +39,7 @@ module ActiveRecord
BLACKLISTED_ARRAY_METHODS = [
:compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!,
:shuffle!, :slice!, :sort!, :sort_by!, :delete_if,
- :keep_if, :pop, :shift, :delete_at, :compact, :select!
+ :keep_if, :pop, :shift, :delete_at, :select!
].to_set # :nodoc:
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 576a32bf75..6020aa238f 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -46,7 +46,7 @@ module ActiveRecord
# # returns the first item or returns a new instance (requires you call .save to persist against the database).
#
# Person.where(name: 'Spartacus', rating: 4).first_or_create
- # # returns the first item or creates it and returns it, available since Rails 3.2.1.
+ # # returns the first item or creates it and returns it.
#
# ==== Alternatives for +find+
#
@@ -57,10 +57,10 @@ module ActiveRecord
# # returns a chainable list of instances with only the mentioned fields.
#
# Person.where(name: 'Spartacus', rating: 4).ids
- # # returns an Array of ids, available since Rails 3.2.1.
+ # # returns an Array of ids.
#
# Person.where(name: 'Spartacus', rating: 4).pluck(:field1, :field2)
- # # returns an Array of the required fields, available since Rails 3.1.
+ # # returns an Array of the required fields.
def find(*args)
if block_given?
to_a.find(*args) { |*block_args| yield(*block_args) }
@@ -111,23 +111,11 @@ module ActiveRecord
# Find the first record (or first N records if a parameter is supplied).
# If no order is defined it will order by primary key.
#
- # Person.first # returns the first object fetched by SELECT * FROM people
+ # Person.first # returns the first object fetched by SELECT * FROM people ORDER BY people.id LIMIT 1
# Person.where(["user_name = ?", user_name]).first
# Person.where(["user_name = :u", { u: user_name }]).first
# Person.order("created_on DESC").offset(5).first
- # Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
- #
- # ==== Rails 3
- #
- # Person.first # SELECT "people".* FROM "people" LIMIT 1
- #
- # NOTE: Rails 3 may not order this query by the primary key and the order
- # will depend on the database implementation. In order to ensure that behavior,
- # use <tt>User.order(:id).first</tt> instead.
- #
- # ==== Rails 4
- #
- # Person.first # SELECT "people".* FROM "people" ORDER BY "people"."id" ASC LIMIT 1
+ # Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
#
def first(limit = nil)
if limit
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index 65b607ff1c..dd8f0aa298 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -1,5 +1,4 @@
require 'active_support/core_ext/hash/keys'
-require "set"
module ActiveRecord
class Relation
@@ -51,7 +50,7 @@ module ActiveRecord
NORMAL_VALUES = Relation::VALUE_METHODS -
Relation::CLAUSE_METHODS -
- [:joins, :order, :reverse_order, :lock, :create_with, :reordering] # :nodoc:
+ [:includes, :preload, :joins, :order, :reverse_order, :lock, :create_with, :reordering] # :nodoc:
def normal_values
NORMAL_VALUES
@@ -76,6 +75,7 @@ module ActiveRecord
merge_multi_values
merge_single_values
merge_clauses
+ merge_preloads
merge_joins
relation
@@ -83,6 +83,27 @@ module ActiveRecord
private
+ def merge_preloads
+ return if other.preload_values.empty? && other.includes_values.empty?
+
+ if other.klass == relation.klass
+ relation.preload! other.preload_values unless other.preload_values.empty?
+ relation.includes! other.includes_values unless other.includes_values.empty?
+ else
+ reflection = relation.klass.reflect_on_all_associations.find do |r|
+ r.class_name == other.klass.name
+ end || return
+
+ unless other.preload_values.empty?
+ relation.preload! reflection.name => other.preload_values
+ end
+
+ unless other.includes_values.empty?
+ relation.includes! reflection.name => other.includes_values
+ end
+ end
+ end
+
def merge_joins
return if other.joins_values.blank?
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 69ce5cdc2a..f85dc35e89 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -587,7 +587,7 @@ module ActiveRecord
#
# The two relations must be structurally compatible: they must be scoping the same model, and
# they must differ only by +where+ (if no +group+ has been defined) or +having+ (if a +group+ is
- # present). Neither relation may have a +limit+, +offset+, or +uniq+ set.
+ # present). Neither relation may have a +limit+, +offset+, or +distinct+ set.
#
# Post.where("id = 1").or(Post.where("id = 2"))
# # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'id = 2'))
@@ -790,6 +790,7 @@ module ActiveRecord
spawn.distinct!(value)
end
alias uniq distinct
+ deprecate uniq: :distinct
# Like #distinct, but modifies relation in place.
def distinct!(value = true) # :nodoc:
@@ -797,6 +798,7 @@ module ActiveRecord
self
end
alias uniq! distinct!
+ deprecate uniq!: :distinct!
# Used to extend a scope with additional methods, either through
# a module or through a block provided.
@@ -999,15 +1001,13 @@ module ActiveRecord
end
def arel_columns(columns)
- if from_clause.value
- columns
- else
- columns.map do |field|
- if (Symbol === field || String === field) && columns_hash.key?(field.to_s)
- arel_table[field]
- else
- field
- end
+ columns.map do |field|
+ if (Symbol === field || String === field) && columns_hash.key?(field.to_s) && !from_clause.value
+ arel_table[field]
+ elsif Symbol === field
+ connection.quote_table_name(field.to_s)
+ else
+ field
end
end
end
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index c7f55ebaa1..ba75ffa5a1 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -11,17 +11,15 @@ module ActiveRecord
protected
- # Accepts an array, hash, or string of SQL conditions and sanitizes
+ # Accepts an array or string of SQL conditions and sanitizes
# them into a valid SQL fragment for a WHERE clause.
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
- # { name: "foo'bar", group_id: 4 } returns "name='foo''bar' and group_id='4'"
# "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
def sanitize_sql_for_conditions(condition, table_name = self.table_name)
return nil if condition.blank?
case condition
when Array; sanitize_sql_array(condition)
- when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
else condition
end
end
@@ -121,9 +119,9 @@ module ActiveRecord
end
def replace_named_bind_variables(statement, bind_vars) #:nodoc:
- statement.gsub(/(:?):([a-zA-Z]\w*)/) do
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do |match|
if $1 == ':' # skip postgresql casts
- $& # return the whole match
+ match # return the whole match
elsif bind_vars.include?(match = $2.to_sym)
replace_bind_variable(bind_vars[match])
else
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index a4a986e6ed..c5910fa1ad 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -1,5 +1,4 @@
require 'stringio'
-require 'active_support/core_ext/big_decimal'
module ActiveRecord
# = Active Record Schema Dumper
diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb
index b5038104ac..cb47bf23f7 100644
--- a/activerecord/lib/active_record/schema_migration.rb
+++ b/activerecord/lib/active_record/schema_migration.rb
@@ -1,6 +1,5 @@
require 'active_record/scoping/default'
require 'active_record/scoping/named'
-require 'active_record/base'
module ActiveRecord
class SchemaMigration < ActiveRecord::Base
diff --git a/activerecord/lib/active_record/suppressor.rb b/activerecord/lib/active_record/suppressor.rb
index b0b86865fd..b3644bf569 100644
--- a/activerecord/lib/active_record/suppressor.rb
+++ b/activerecord/lib/active_record/suppressor.rb
@@ -37,8 +37,7 @@ module ActiveRecord
end
end
- # Ignore saving events if we're in suppression mode.
- def save!(*args) # :nodoc:
+ def create_or_update(*args) # :nodoc:
SuppressorRegistry.suppressed[self.class.name] ? true : super
end
end
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index eafbb2c249..673386f0d9 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -59,6 +59,7 @@ module ActiveRecord
args = prepare_command_options('mysqldump')
args.concat(["--result-file", "#{filename}"])
args.concat(["--no-data"])
+ args.concat(["--routines"])
args.concat(["#{configuration['database']}"])
unless Kernel.system(*args)
$stderr.puts "Could not dump the database structure. "\
@@ -130,15 +131,21 @@ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
end
def prepare_command_options(command)
- args = [command]
- args.concat(['--user', configuration['username']]) if configuration['username']
- args << "--password=#{configuration['password']}" if configuration['password']
- args.concat(['--default-character-set', configuration['encoding']]) if configuration['encoding']
- configuration.slice('host', 'port', 'socket').each do |k, v|
- args.concat([ "--#{k}", v.to_s ]) if v
- end
-
- args
+ args = {
+ 'host' => '--host',
+ 'port' => '--port',
+ 'socket' => '--socket',
+ 'username' => '--user',
+ 'password' => '--password',
+ 'encoding' => '--default-character-set',
+ 'sslca' => '--ssl-ca',
+ 'sslcert' => '--ssl-cert',
+ 'sslcapath' => '--ssl-capath',
+ 'sslcipher' => '--ssh-cipher',
+ 'sslkey' => '--ssl-key'
+ }.map { |opt, arg| "#{arg}=#{configuration[opt]}" if configuration[opt] }.compact
+
+ [command, *args]
end
end
end
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 311dacb449..6f2def0df1 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -196,9 +196,9 @@ module ActiveRecord
# automatically released. The following example demonstrates the problem:
#
# Model.connection.transaction do # BEGIN
- # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
+ # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
# Model.connection.create_table(...) # active_record_1 now automatically released
- # end # RELEASE savepoint active_record_1
+ # end # RELEASE SAVEPOINT active_record_1
# # ^^^^ BOOM! database error!
# end
#
diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb
index 2a1b04ac7f..c5040c6d3b 100644
--- a/activerecord/lib/active_record/type/integer.rb
+++ b/activerecord/lib/active_record/type/integer.rb
@@ -46,18 +46,21 @@ module ActiveRecord
def ensure_in_range(value)
unless range.cover?(value)
- raise RangeError, "#{value} is out of range for #{self.class} with limit #{limit || DEFAULT_LIMIT}"
+ raise RangeError, "#{value} is out of range for #{self.class} with limit #{_limit}"
end
end
def max_value
- limit = self.limit || DEFAULT_LIMIT
- 1 << (limit * 8 - 1) # 8 bits per byte with one bit for sign
+ 1 << (_limit * 8 - 1) # 8 bits per byte with one bit for sign
end
def min_value
-max_value
end
+
+ def _limit
+ self.limit || DEFAULT_LIMIT
+ end
end
end
end
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index 4762ef43b5..9903cd3c0b 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -145,6 +145,15 @@ class MysqlConnectionTest < ActiveRecord::TestCase
end
end
+ def test_mysql_strict_mode_specified_default
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(orig_connection.merge({strict: :default}))
+ global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode"
+ session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode"
+ assert_equal global_sql_mode.rows, session_sql_mode.rows
+ end
+ end
+
def test_mysql_set_session_variable
run_without_connection do |orig_connection|
ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => 3}}))
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index a8b39b21d4..c4e0278c89 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -84,6 +84,15 @@ class MysqlConnectionTest < ActiveRecord::TestCase
end
end
+ def test_mysql_strict_mode_specified_default
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(orig_connection.merge({strict: :default}))
+ global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode"
+ session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode"
+ assert_equal global_sql_mode.rows, session_sql_mode.rows
+ end
+ end
+
def test_mysql_set_session_variable
run_without_connection do |orig_connection|
ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => 3}}))
diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
index 41e9572907..7057848eb0 100644
--- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
@@ -6,7 +6,15 @@ class PostgresqlPointTest < ActiveRecord::TestCase
include ConnectionHelper
include SchemaDumpingHelper
- class PostgresqlPoint < ActiveRecord::Base; end
+ class PostgresqlPoint < ActiveRecord::Base
+ attribute :x, :rails_5_1_point
+ attribute :y, :rails_5_1_point
+ attribute :z, :rails_5_1_point
+ attribute :array_of_points, :rails_5_1_point, array: true
+ attribute :legacy_x, :legacy_point
+ attribute :legacy_y, :legacy_point
+ attribute :legacy_z, :legacy_point
+ end
def setup
@connection = ActiveRecord::Base.connection
@@ -14,11 +22,27 @@ class PostgresqlPointTest < ActiveRecord::TestCase
t.point :x
t.point :y, default: [12.2, 13.3]
t.point :z, default: "(14.4,15.5)"
+ t.point :array_of_points, array: true
+ t.point :legacy_x
+ t.point :legacy_y, default: [12.2, 13.3]
+ t.point :legacy_z, default: "(14.4,15.5)"
+ end
+ @connection.create_table('deprecated_points') do |t|
+ t.point :x
end
end
teardown do
@connection.drop_table 'postgresql_points', if_exists: true
+ @connection.drop_table 'deprecated_points', if_exists: true
+ end
+
+ class DeprecatedPoint < ActiveRecord::Base; end
+
+ def test_deprecated_legacy_type
+ assert_deprecated do
+ DeprecatedPoint.new
+ end
end
def test_column
@@ -32,11 +56,11 @@ class PostgresqlPointTest < ActiveRecord::TestCase
end
def test_default
- assert_equal [12.2, 13.3], PostgresqlPoint.column_defaults['y']
- assert_equal [12.2, 13.3], PostgresqlPoint.new.y
+ assert_equal ActiveRecord::Point.new(12.2, 13.3), PostgresqlPoint.column_defaults['y']
+ assert_equal ActiveRecord::Point.new(12.2, 13.3), PostgresqlPoint.new.y
- assert_equal [14.4, 15.5], PostgresqlPoint.column_defaults['z']
- assert_equal [14.4, 15.5], PostgresqlPoint.new.z
+ assert_equal ActiveRecord::Point.new(14.4, 15.5), PostgresqlPoint.column_defaults['z']
+ assert_equal ActiveRecord::Point.new(14.4, 15.5), PostgresqlPoint.new.z
end
def test_schema_dumping
@@ -49,22 +73,95 @@ class PostgresqlPointTest < ActiveRecord::TestCase
def test_roundtrip
PostgresqlPoint.create! x: [10, 25.2]
record = PostgresqlPoint.first
- assert_equal [10, 25.2], record.x
+ assert_equal ActiveRecord::Point.new(10, 25.2), record.x
- record.x = [1.1, 2.2]
+ record.x = ActiveRecord::Point.new(1.1, 2.2)
record.save!
assert record.reload
- assert_equal [1.1, 2.2], record.x
+ assert_equal ActiveRecord::Point.new(1.1, 2.2), record.x
end
def test_mutation
- p = PostgresqlPoint.create! x: [10, 20]
+ p = PostgresqlPoint.create! x: ActiveRecord::Point.new(10, 20)
+
+ p.x.y = 25
+ p.save!
+ p.reload
+
+ assert_equal ActiveRecord::Point.new(10.0, 25.0), p.x
+ assert_not p.changed?
+ end
+
+ def test_array_assignment
+ p = PostgresqlPoint.new(x: [1, 2])
+
+ assert_equal ActiveRecord::Point.new(1, 2), p.x
+ end
+
+ def test_string_assignment
+ p = PostgresqlPoint.new(x: "(1, 2)")
+
+ assert_equal ActiveRecord::Point.new(1, 2), p.x
+ end
+
+ def test_array_of_points_round_trip
+ expected_value = [
+ ActiveRecord::Point.new(1, 2),
+ ActiveRecord::Point.new(2, 3),
+ ActiveRecord::Point.new(3, 4),
+ ]
+ p = PostgresqlPoint.new(array_of_points: expected_value)
+
+ assert_equal expected_value, p.array_of_points
+ p.save!
+ p.reload
+ assert_equal expected_value, p.array_of_points
+ end
+
+ def test_legacy_column
+ column = PostgresqlPoint.columns_hash["legacy_x"]
+ assert_equal :point, column.type
+ assert_equal "point", column.sql_type
+ assert_not column.array?
+
+ type = PostgresqlPoint.type_for_attribute("legacy_x")
+ assert_not type.binary?
+ end
+
+ def test_legacy_default
+ assert_equal [12.2, 13.3], PostgresqlPoint.column_defaults['legacy_y']
+ assert_equal [12.2, 13.3], PostgresqlPoint.new.legacy_y
+
+ assert_equal [14.4, 15.5], PostgresqlPoint.column_defaults['legacy_z']
+ assert_equal [14.4, 15.5], PostgresqlPoint.new.legacy_z
+ end
+
+ def test_legacy_schema_dumping
+ output = dump_table_schema("postgresql_points")
+ assert_match %r{t\.point\s+"legacy_x"$}, output
+ assert_match %r{t\.point\s+"legacy_y",\s+default: \[12\.2, 13\.3\]$}, output
+ assert_match %r{t\.point\s+"legacy_z",\s+default: \[14\.4, 15\.5\]$}, output
+ end
+
+ def test_legacy_roundtrip
+ PostgresqlPoint.create! legacy_x: [10, 25.2]
+ record = PostgresqlPoint.first
+ assert_equal [10, 25.2], record.legacy_x
+
+ record.legacy_x = [1.1, 2.2]
+ record.save!
+ assert record.reload
+ assert_equal [1.1, 2.2], record.legacy_x
+ end
+
+ def test_legacy_mutation
+ p = PostgresqlPoint.create! legacy_x: [10, 20]
- p.x[1] = 25
+ p.legacy_x[1] = 25
p.save!
p.reload
- assert_equal [10.0, 25.0], p.x
+ assert_equal [10.0, 25.0], p.legacy_x
assert_not p.changed?
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/collation_test.rb b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
new file mode 100644
index 0000000000..39e4620bc9
--- /dev/null
+++ b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
@@ -0,0 +1,53 @@
+require "cases/helper"
+require 'support/schema_dumping_helper'
+
+class SQLite3CollationTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table :collation_table_sqlite3, force: true do |t|
+ t.string :string_nocase, collation: 'NOCASE'
+ t.text :text_rtrim, collation: 'RTRIM'
+ end
+ end
+
+ def teardown
+ @connection.drop_table :collation_table_sqlite3, if_exists: true
+ end
+
+ test "string column with collation" do
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'string_nocase' }
+ assert_equal :string, column.type
+ assert_equal 'NOCASE', column.collation
+ end
+
+ test "text column with collation" do
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'text_rtrim' }
+ assert_equal :text, column.type
+ assert_equal 'RTRIM', column.collation
+ end
+
+ test "add column with collation" do
+ @connection.add_column :collation_table_sqlite3, :title, :string, collation: 'RTRIM'
+
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'title' }
+ assert_equal :string, column.type
+ assert_equal 'RTRIM', column.collation
+ end
+
+ test "change column with collation" do
+ @connection.add_column :collation_table_sqlite3, :description, :string
+ @connection.change_column :collation_table_sqlite3, :description, :text, collation: 'RTRIM'
+
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'description' }
+ assert_equal :text, column.type
+ assert_equal 'RTRIM', column.collation
+ end
+
+ test "schema dump includes collation" do
+ output = dump_table_schema("collation_table_sqlite3")
+ assert_match %r{t.string\s+"string_nocase",\s+collation: "NOCASE"$}, output
+ assert_match %r{t.text\s+"text_rtrim",\s+collation: "RTRIM"$}, output
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 27f4ba8eb6..6be9795603 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -421,14 +421,14 @@ module ActiveRecord
end
def test_statement_closed
- db = SQLite3::Database.new(ActiveRecord::Base.
+ db = ::SQLite3::Database.new(ActiveRecord::Base.
configurations['arunit']['database'])
- statement = SQLite3::Statement.new(db,
+ statement = ::SQLite3::Statement.new(db,
'CREATE TABLE statement_test (number integer not null)')
- statement.stubs(:step).raises(SQLite3::BusyException, 'busy')
+ statement.stubs(:step).raises(::SQLite3::BusyException, 'busy')
statement.stubs(:columns).once.returns([])
statement.expects(:close).once
- SQLite3::Statement.stubs(:new).returns(statement)
+ ::SQLite3::Statement.stubs(:new).returns(statement)
assert_raises ActiveRecord::StatementInvalid do
@conn.exec_query 'select * from statement_test'
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 0ecf2ddfd1..ffbf60e390 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1167,7 +1167,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_no_queries { assert client.accounts.empty? }
end
- def test_preloading_has_many_through_with_uniq
+ def test_preloading_has_many_through_with_distinct
mary = Author.includes(:unique_categorized_posts).where(:id => authors(:mary).id).first
assert_equal 1, mary.unique_categorized_posts.length
assert_equal 1, mary.unique_categorized_post_ids.length
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index aea9207bfe..e584c94ad8 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -83,6 +83,16 @@ class DeveloperWithSymbolClassName < Developer
has_and_belongs_to_many :projects, class_name: :ProjectWithSymbolsForKeys
end
+class DeveloperWithExtendOption < Developer
+ module NamedExtension
+ def category
+ 'sns'
+ end
+ end
+
+ has_and_belongs_to_many :projects, extend: NamedExtension
+end
+
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
:parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings, :computers
@@ -234,7 +244,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal developers, new_project.developers
end
- def test_habtm_unique_order_preserved
+ def test_habtm_distinct_order_preserved
assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).non_unique_developers
assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).developers
end
@@ -339,7 +349,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 'Yet Another Testing Title', another_post.title
end
- def test_uniq_after_the_fact
+ def test_distinct_after_the_fact
dev = developers(:jamis)
dev.projects << projects(:active_record)
dev.projects << projects(:active_record)
@@ -348,13 +358,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, dev.projects.distinct.size
end
- def test_uniq_before_the_fact
+ def test_distinct_before_the_fact
projects(:active_record).developers << developers(:jamis)
projects(:active_record).developers << developers(:david)
assert_equal 3, projects(:active_record, :reload).developers.size
end
- def test_uniq_option_prevents_duplicate_push
+ def test_distinct_option_prevents_duplicate_push
project = projects(:active_record)
project.developers << developers(:jamis)
project.developers << developers(:david)
@@ -365,7 +375,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 3, project.developers.size
end
- def test_uniq_when_association_already_loaded
+ def test_distinct_when_association_already_loaded
project = projects(:active_record)
project.developers << [ developers(:jamis), developers(:david), developers(:jamis), developers(:david) ]
assert_equal 3, Project.includes(:developers).find(project.id).developers.size
@@ -577,6 +587,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal developers(:poor_jamis), projects(:active_record).developers.where("salary < 10000").first
end
+ def test_association_with_extend_option
+ eponine = DeveloperWithExtendOption.create(name: 'Eponine')
+ assert_equal 'sns', eponine.projects.category
+ end
+
def test_replace_with_less
david = developers(:david)
david.projects = [projects(:action_controller)]
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 9734ea2217..1d545af5a5 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -960,7 +960,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal 1, category.categorizations.where(:special => true).count
end
- def test_joining_has_many_through_with_uniq
+ def test_joining_has_many_through_with_distinct
mary = Author.joins(:unique_categorized_posts).where(:id => authors(:mary).id).first
assert_equal 1, mary.unique_categorized_posts.length
assert_equal 1, mary.unique_categorized_post_ids.length
@@ -1040,14 +1040,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- def test_save_should_not_raise_exception_when_join_record_has_errors
- repair_validations(Categorization) do
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
- c = Category.create(:name => 'Fishing', :authors => [Author.first])
- c.save
- end
- end
-
def test_assign_array_to_new_record_builds_join_records
c = Category.new(:name => 'Fishing', :authors => [Author.first])
assert_equal 1, c.categorizations.size
@@ -1072,11 +1064,11 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- def test_create_bang_returns_falsy_when_join_record_has_errors
+ def test_save_returns_falsy_when_join_record_has_errors
repair_validations(Categorization) do
Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
c = Category.new(:name => 'Fishing', :authors => [Author.first])
- assert !c.save
+ assert_not c.save
end
end
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 213be50e67..5575419c35 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -35,12 +35,12 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert categories(:sti_test).authors.include?(authors(:mary))
end
- def test_has_many_uniq_through_join_model
+ def test_has_many_distinct_through_join_model
assert_equal 2, authors(:mary).categorized_posts.size
assert_equal 1, authors(:mary).unique_categorized_posts.size
end
- def test_has_many_uniq_through_count
+ def test_has_many_distinct_through_count
author = authors(:mary)
assert !authors(:mary).unique_categorized_posts.loaded?
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count }
@@ -49,7 +49,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert !authors(:mary).unique_categorized_posts.loaded?
end
- def test_has_many_uniq_through_find
+ def test_has_many_distinct_through_find
assert_equal 1, authors(:mary).unique_categorized_posts.to_a.size
end
@@ -625,7 +625,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_equal [comments(:does_it_hurt)], authors(:david).special_post_comments
end
- def test_uniq_has_many_through_should_retain_order
+ def test_distinct_has_many_through_should_retain_order
comment_ids = authors(:david).comments.map(&:id)
assert_equal comment_ids.sort, authors(:david).ordered_uniq_comments.map(&:id)
assert_equal comment_ids.sort.reverse, authors(:david).ordered_uniq_comments_desc.map(&:id)
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index 927d7950a5..eeda9335ad 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -125,6 +125,22 @@ module ActiveRecord
assert_equal "from user", model.wibble
end
+ test "procs for default values" do
+ klass = Class.new(OverloadedType) do
+ @@counter = 0
+ attribute :counter, :integer, default: -> { @@counter += 1 }
+ end
+
+ assert_equal 1, klass.new.counter
+ assert_equal 2, klass.new.counter
+ end
+
+ test "user provided defaults are persisted even if unchanged" do
+ model = OverloadedType.create!
+
+ assert_equal "the overloaded default", model.reload.string_with_default
+ end
+
if current_adapter?(:PostgreSQLAdapter)
test "arrays types can be specified" do
klass = Class.new(OverloadedType) do
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 4306738670..31c31e4329 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1411,15 +1411,13 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_uniq_delegates_to_scoped
- scope = stub
- Bird.stubs(:all).returns(mock(:uniq => scope))
- assert_equal scope, Bird.uniq
+ assert_deprecated do
+ assert_equal Bird.all.distinct, Bird.uniq
+ end
end
def test_distinct_delegates_to_scoped
- scope = stub
- Bird.stubs(:all).returns(mock(:distinct => scope))
- assert_equal scope, Bird.distinct
+ assert_equal Bird.all.distinct, Bird.distinct
end
def test_table_name_with_2_abstract_subclasses
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 8fc996ee74..cb4681109c 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -359,7 +359,10 @@ class CalculationsTest < ActiveRecord::TestCase
def test_count_with_distinct
assert_equal 4, Account.select(:credit_limit).distinct.count
- assert_equal 4, Account.select(:credit_limit).uniq.count
+
+ assert_deprecated do
+ assert_equal 4, Account.select(:credit_limit).uniq.count
+ end
end
def test_count_with_aliased_attribute
@@ -504,8 +507,8 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [ topic.written_on ], relation.pluck(:written_on)
end
- def test_pluck_and_uniq
- assert_equal [50, 53, 55, 60], Account.order(:credit_limit).uniq.pluck(:credit_limit)
+ def test_pluck_and_distinct
+ assert_equal [50, 53, 55, 60], Account.order(:credit_limit).distinct.pluck(:credit_limit)
end
def test_pluck_in_relation
@@ -629,6 +632,27 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [part.id], ShipPart.joins(:trinkets).pluck(:id)
end
+ def test_pluck_loaded_relation
+ companies = Company.order(:id).limit(3).load
+ assert_no_queries do
+ assert_equal ['37signals', 'Summit', 'Microsoft'], companies.pluck(:name)
+ end
+ end
+
+ def test_pluck_loaded_relation_multiple_columns
+ companies = Company.order(:id).limit(3).load
+ assert_no_queries do
+ assert_equal [[1, '37signals'], [2, 'Summit'], [3, 'Microsoft']], companies.pluck(:id, :name)
+ end
+ end
+
+ def test_pluck_loaded_relation_sql_fragment
+ companies = Company.order(:name).limit(3).load
+ assert_queries 1 do
+ assert_equal ['37signals', 'Apex', 'Ex Nihilo'], companies.pluck('DISTINCT name')
+ end
+ end
+
def test_grouped_calculation_with_polymorphic_relation
part = ShipPart.create!(name: "has trinket")
part.trinkets.create!
diff --git a/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
index 662e19f35e..580568c8ac 100644
--- a/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
+++ b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
@@ -6,7 +6,7 @@ module ActiveRecord
class Pool < ConnectionPool
def insert_connection_for_test!(c)
synchronize do
- @connections << c
+ adopt_connection(c)
@available.add c
end
end
@@ -24,7 +24,9 @@ module ActiveRecord
def test_lease_twice
assert @adapter.lease, 'should lease adapter'
- assert_not @adapter.lease, 'should not lease adapter'
+ assert_raises(ActiveRecordError) do
+ @adapter.lease
+ end
end
def test_expire_mutates_in_use
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index b72f8ca88c..9b1865e8bb 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -46,6 +46,52 @@ module ActiveRecord
def test_connection_pools
assert_equal([@pool], @handler.connection_pools)
end
+
+ if Process.respond_to?(:fork)
+ def test_connection_pool_per_pid
+ object_id = ActiveRecord::Base.connection.object_id
+
+ rd, wr = IO.pipe
+ rd.binmode
+ wr.binmode
+
+ pid = fork {
+ rd.close
+ wr.write Marshal.dump ActiveRecord::Base.connection.object_id
+ wr.close
+ exit!
+ }
+
+ wr.close
+
+ Process.waitpid pid
+ assert_not_equal object_id, Marshal.load(rd.read)
+ rd.close
+ end
+
+ def test_retrieve_connection_pool_copies_schema_cache_from_ancestor_pool
+ @pool.schema_cache = @pool.connection.schema_cache
+ @pool.schema_cache.add('posts')
+
+ rd, wr = IO.pipe
+ rd.binmode
+ wr.binmode
+
+ pid = fork {
+ rd.close
+ pool = @handler.retrieve_connection_pool(@klass)
+ wr.write Marshal.dump pool.schema_cache.size
+ wr.close
+ exit!
+ }
+
+ wr.close
+
+ Process.waitpid pid
+ assert_equal @pool.schema_cache.size, Marshal.load(rd.read)
+ rd.close
+ end
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
index 05c57985a1..7566863653 100644
--- a/activerecord/test/cases/connection_adapters/type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
@@ -81,7 +81,11 @@ module ActiveRecord
def test_bigint_limit
cast_type = @connection.type_map.lookup("bigint")
- assert_equal 8, cast_type.limit
+ if current_adapter?(:OracleAdapter)
+ assert_equal 19, cast_type.limit
+ else
+ assert_equal 8, cast_type.limit
+ end
end
def test_decimal_without_scale
diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb
index bab624b78a..dff6ea0fb0 100644
--- a/activerecord/test/cases/connection_management_test.rb
+++ b/activerecord/test/cases/connection_management_test.rb
@@ -26,29 +26,6 @@ module ActiveRecord
assert ActiveRecord::Base.connection_handler.active_connections?
end
- if Process.respond_to?(:fork)
- def test_connection_pool_per_pid
- object_id = ActiveRecord::Base.connection.object_id
-
- rd, wr = IO.pipe
- rd.binmode
- wr.binmode
-
- pid = fork {
- rd.close
- wr.write Marshal.dump ActiveRecord::Base.connection.object_id
- wr.close
- exit!
- }
-
- wr.close
-
- Process.waitpid pid
- assert_not_equal object_id, Marshal.load(rd.read)
- rd.close
- end
- end
-
def test_app_delegation
manager = ConnectionManagement.new(@app)
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index f5928814a3..c905772193 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -100,7 +100,7 @@ module ActiveRecord
t = Thread.new { @pool.checkout }
# make sure our thread is in the timeout section
- Thread.pass until t.status == "sleep"
+ Thread.pass until @pool.num_waiting_in_queue == 1
connection = cs.first
connection.close
@@ -112,7 +112,7 @@ module ActiveRecord
t = Thread.new { @pool.checkout }
# make sure our thread is in the timeout section
- Thread.pass until t.status == "sleep"
+ Thread.pass until @pool.num_waiting_in_queue == 1
connection = cs.first
@pool.remove connection
@@ -234,7 +234,7 @@ module ActiveRecord
mutex.synchronize { errors << e }
end
}
- Thread.pass until t.status == "sleep"
+ Thread.pass until @pool.num_waiting_in_queue == i
t
end
@@ -271,7 +271,7 @@ module ActiveRecord
mutex.synchronize { errors << e }
end
}
- Thread.pass until t.status == "sleep"
+ Thread.pass until @pool.num_waiting_in_queue == i
t
end
@@ -356,6 +356,170 @@ module ActiveRecord
pool.checkin connection
end
+
+ def test_concurrent_connection_establishment
+ assert_operator @pool.connections.size, :<=, 1
+
+ all_threads_in_new_connection = ActiveSupport::Concurrency::Latch.new(@pool.size - @pool.connections.size)
+ all_go = ActiveSupport::Concurrency::Latch.new
+
+ @pool.singleton_class.class_eval do
+ define_method(:new_connection) do
+ all_threads_in_new_connection.release
+ all_go.await
+ super()
+ end
+ end
+
+ connecting_threads = []
+ @pool.size.times do
+ connecting_threads << Thread.new { @pool.checkout }
+ end
+
+ begin
+ Timeout.timeout(5) do
+ # the kernel of the whole test is here, everything else is just scaffolding,
+ # this latch will not be released unless conn. pool allows for concurrent
+ # connection creation
+ all_threads_in_new_connection.await
+ end
+ rescue Timeout::Error
+ flunk 'pool unable to establish connections concurrently or implementation has ' <<
+ 'changed, this test then needs to patch a different :new_connection method'
+ ensure
+ # clean up the threads
+ all_go.release
+ connecting_threads.map(&:join)
+ end
+ end
+
+ def test_non_bang_disconnect_and_clear_reloadable_connections_throw_exception_if_threads_dont_return_their_conns
+ @pool.checkout_timeout = 0.001 # no need to delay test suite by waiting the whole full default timeout
+ [:disconnect, :clear_reloadable_connections].each do |group_action_method|
+ @pool.with_connection do |connection|
+ assert_raises(ExclusiveConnectionTimeoutError) do
+ Thread.new { @pool.send(group_action_method) }.join
+ end
+ end
+ end
+ end
+
+ def test_disconnect_and_clear_reloadable_connections_attempt_to_wait_for_threads_to_return_their_conns
+ [:disconnect, :disconnect!, :clear_reloadable_connections, :clear_reloadable_connections!].each do |group_action_method|
+ begin
+ thread = timed_join_result = nil
+ @pool.with_connection do |connection|
+ thread = Thread.new { @pool.send(group_action_method) }
+
+ # give the other `thread` some time to get stuck in `group_action_method`
+ timed_join_result = thread.join(0.3)
+ # thread.join # => `nil` means the other thread hasn't finished running and is still waiting for us to
+ # release our connection
+ assert_nil timed_join_result
+
+ # assert that since this is within default timeout our connection hasn't been forcefully taken away from us
+ assert @pool.active_connection?
+ end
+ ensure
+ thread.join if thread && !timed_join_result # clean up the other thread
+ end
+ end
+ end
+
+ def test_bang_versions_of_disconnect_and_clear_reloadable_connections_if_unable_to_aquire_all_connections_proceed_anyway
+ @pool.checkout_timeout = 0.001 # no need to delay test suite by waiting the whole full default timeout
+ [:disconnect!, :clear_reloadable_connections!].each do |group_action_method|
+ @pool.with_connection do |connection|
+ Thread.new { @pool.send(group_action_method) }.join
+ # assert connection has been forcefully taken away from us
+ assert_not @pool.active_connection?
+ end
+ end
+ end
+
+ def test_disconnect_and_clear_reloadable_connections_are_able_to_preempt_other_waiting_threads
+ with_single_connection_pool do |pool|
+ [:disconnect, :disconnect!, :clear_reloadable_connections, :clear_reloadable_connections!].each do |group_action_method|
+ conn = pool.connection # drain the only available connection
+ second_thread_done = ActiveSupport::Concurrency::Latch.new
+
+ # create a first_thread and let it get into the FIFO queue first
+ first_thread = Thread.new do
+ pool.with_connection { second_thread_done.await }
+ end
+
+ # wait for first_thread to get in queue
+ Thread.pass until pool.num_waiting_in_queue == 1
+
+ # create a different, later thread, that will attempt to do a "group action",
+ # but because of the group action semantics it should be able to preempt the
+ # first_thread when a connection is made available
+ second_thread = Thread.new do
+ pool.send(group_action_method)
+ second_thread_done.release
+ end
+
+ # wait for second_thread to get in queue
+ Thread.pass until pool.num_waiting_in_queue == 2
+
+ # return the only available connection
+ pool.checkin(conn)
+
+ # if the second_thread is not able to preempt the first_thread,
+ # they will temporarily (until either of them timeouts with ConnectionTimeoutError)
+ # deadlock and a join(2) timeout will be reached
+ failed = true unless second_thread.join(2)
+
+ #--- post test clean up start
+ second_thread_done.release if failed
+
+ # after `pool.disconnect()` the first thread will be left stuck in queue, no need to wait for
+ # it to timeout with ConnectionTimeoutError
+ if (group_action_method == :disconnect || group_action_method == :disconnect!) && pool.num_waiting_in_queue > 0
+ pool.with_connection {} # create a new connection in case there are threads still stuck in a queue
+ end
+
+ first_thread.join
+ second_thread.join
+ #--- post test clean up end
+
+ flunk "#{group_action_method} is not able to preempt other waiting threads" if failed
+ end
+ end
+ end
+
+ def test_clear_reloadable_connections_creates_new_connections_for_waiting_threads_if_necessary
+ with_single_connection_pool do |pool|
+ conn = pool.connection # drain the only available connection
+ def conn.requires_reloading? # make sure it gets removed from the pool by clear_reloadable_connections
+ true
+ end
+
+ stuck_thread = Thread.new do
+ pool.with_connection {}
+ end
+
+ # wait for stuck_thread to get in queue
+ Thread.pass until pool.num_waiting_in_queue == 1
+
+ pool.clear_reloadable_connections
+
+ unless stuck_thread.join(2)
+ flunk 'clear_reloadable_connections must not let other connection waiting threads get stuck in queue'
+ end
+
+ assert_equal 0, pool.num_waiting_in_queue
+ end
+ end
+
+ private
+ def with_single_connection_pool
+ one_conn_spec = ActiveRecord::Base.connection_pool.spec.dup
+ one_conn_spec.config[:pool] = 1 # this is safe to do, because .dupped ConnectionSpecification also auto-dups its config
+ yield(pool = ConnectionPool.new(one_conn_spec))
+ ensure
+ pool.disconnect! if pool
+ end
end
end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 3a7cc572e6..216f228142 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -623,32 +623,6 @@ class DirtyTest < ActiveRecord::TestCase
end
end
- test "defaults with type that implements `serialize`" do
- type = Class.new(ActiveRecord::Type::Value) do
- def cast(value)
- value.to_i
- end
-
- def serialize(value)
- value.to_s
- end
- end
-
- model_class = Class.new(ActiveRecord::Base) do
- self.table_name = 'numeric_data'
- attribute :foo, type.new, default: 1
- end
-
- model = model_class.new
- assert_not model.foo_changed?
-
- model = model_class.new(foo: 1)
- assert_not model.foo_changed?
-
- model = model_class.new(foo: '1')
- assert_not model.foo_changed?
- end
-
test "in place mutation detection" do
pirate = Pirate.create!(catchphrase: "arrrr")
pirate.catchphrase << " matey!"
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index eea184e530..3641826daa 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -9,49 +9,49 @@ class EnumTest < ActiveRecord::TestCase
end
test "query state by predicate" do
- assert @book.proposed?
+ assert @book.published?
assert_not @book.written?
- assert_not @book.published?
+ assert_not @book.proposed?
- assert @book.unread?
+ assert @book.read?
end
test "query state with strings" do
- assert_equal "proposed", @book.status
- assert_equal "unread", @book.read_status
+ assert_equal "published", @book.status
+ assert_equal "read", @book.read_status
end
test "find via scope" do
- assert_equal @book, Book.proposed.first
- assert_equal @book, Book.unread.first
+ assert_equal @book, Book.published.first
+ assert_equal @book, Book.read.first
end
test "find via where with values" do
- proposed, written = Book.statuses[:proposed], Book.statuses[:written]
+ published, written = Book.statuses[:published], Book.statuses[:written]
- assert_equal @book, Book.where(status: proposed).first
+ assert_equal @book, Book.where(status: published).first
refute_equal @book, Book.where(status: written).first
- assert_equal @book, Book.where(status: [proposed]).first
+ assert_equal @book, Book.where(status: [published]).first
refute_equal @book, Book.where(status: [written]).first
- refute_equal @book, Book.where("status <> ?", proposed).first
+ refute_equal @book, Book.where("status <> ?", published).first
assert_equal @book, Book.where("status <> ?", written).first
end
test "find via where with symbols" do
- assert_equal @book, Book.where(status: :proposed).first
+ assert_equal @book, Book.where(status: :published).first
refute_equal @book, Book.where(status: :written).first
- assert_equal @book, Book.where(status: [:proposed]).first
+ assert_equal @book, Book.where(status: [:published]).first
refute_equal @book, Book.where(status: [:written]).first
- refute_equal @book, Book.where.not(status: :proposed).first
+ refute_equal @book, Book.where.not(status: :published).first
assert_equal @book, Book.where.not(status: :written).first
end
test "find via where with strings" do
- assert_equal @book, Book.where(status: "proposed").first
+ assert_equal @book, Book.where(status: "published").first
refute_equal @book, Book.where(status: "written").first
- assert_equal @book, Book.where(status: ["proposed"]).first
+ assert_equal @book, Book.where(status: ["published"]).first
refute_equal @book, Book.where(status: ["written"]).first
- refute_equal @book, Book.where.not(status: "proposed").first
+ refute_equal @book, Book.where.not(status: "published").first
assert_equal @book, Book.where.not(status: "written").first
end
@@ -96,14 +96,14 @@ class EnumTest < ActiveRecord::TestCase
test "enum changed attributes" do
old_status = @book.status
- @book.status = :published
+ @book.status = :proposed
assert_equal old_status, @book.changed_attributes[:status]
end
test "enum changes" do
old_status = @book.status
- @book.status = :published
- assert_equal [old_status, 'published'], @book.changes[:status]
+ @book.status = :proposed
+ assert_equal [old_status, 'proposed'], @book.changes[:status]
end
test "enum attribute was" do
@@ -113,25 +113,25 @@ class EnumTest < ActiveRecord::TestCase
end
test "enum attribute changed" do
- @book.status = :published
+ @book.status = :proposed
assert @book.attribute_changed?(:status)
end
test "enum attribute changed to" do
- @book.status = :published
- assert @book.attribute_changed?(:status, to: 'published')
+ @book.status = :proposed
+ assert @book.attribute_changed?(:status, to: 'proposed')
end
test "enum attribute changed from" do
old_status = @book.status
- @book.status = :published
+ @book.status = :proposed
assert @book.attribute_changed?(:status, from: old_status)
end
test "enum attribute changed from old status to new status" do
old_status = @book.status
- @book.status = :published
- assert @book.attribute_changed?(:status, from: old_status, to: 'published')
+ @book.status = :proposed
+ assert @book.attribute_changed?(:status, from: old_status, to: 'proposed')
end
test "enum didn't change" do
@@ -141,7 +141,7 @@ class EnumTest < ActiveRecord::TestCase
end
test "persist changes that are dirty" do
- @book.status = :published
+ @book.status = :proposed
assert @book.attribute_changed?(:status)
@book.status = :written
assert @book.attribute_changed?(:status)
@@ -149,7 +149,7 @@ class EnumTest < ActiveRecord::TestCase
test "reverted changes that are not dirty" do
old_status = @book.status
- @book.status = :published
+ @book.status = :proposed
assert @book.attribute_changed?(:status)
@book.status = old_status
assert_not @book.attribute_changed?(:status)
@@ -210,9 +210,9 @@ class EnumTest < ActiveRecord::TestCase
test "_before_type_cast returns the enum label (required for form fields)" do
if @book.status_came_from_user?
- assert_equal "proposed", @book.status_before_type_cast
+ assert_equal "published", @book.status_before_type_cast
else
- assert_equal "proposed", @book.status
+ assert_equal "published", @book.status
end
end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 97ba178b4d..03a187ae92 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -16,6 +16,7 @@ require 'models/joke'
require 'models/matey'
require 'models/parrot'
require 'models/pirate'
+require 'models/doubloon'
require 'models/post'
require 'models/randomly_named_c1'
require 'models/reply'
@@ -691,7 +692,7 @@ end
class FoxyFixturesTest < ActiveRecord::TestCase
fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers,
- :developers, :"admin/accounts", :"admin/users", :live_parrots, :dead_parrots
+ :developers, :"admin/accounts", :"admin/users", :live_parrots, :dead_parrots, :books
if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
require 'models/uuid_parent'
@@ -841,6 +842,13 @@ class FoxyFixturesTest < ActiveRecord::TestCase
assert admin_accounts(:signals37).users.include?(admin_users(:david))
assert_equal 2, admin_accounts(:signals37).users.size
end
+
+ def test_resolves_enums
+ assert books(:awdr).published?
+ assert books(:awdr).read?
+ assert books(:rfr).proposed?
+ assert books(:ddd).published?
+ end
end
class ActiveSupportSubclassWithFixturesTest < ActiveRecord::TestCase
@@ -896,3 +904,12 @@ class FixturesWithDefaultScopeTest < ActiveRecord::TestCase
assert_equal "special", bulbs(:special).name
end
end
+
+class FixturesWithAbstractBelongsTo < ActiveRecord::TestCase
+ fixtures :pirates, :doubloons
+
+ test "creates fixtures with belongs_to associations defined in abstract base classes" do
+ assert_not_nil doubloons(:blackbeards_doubloon)
+ assert_equal pirates(:blackbeard), doubloons(:blackbeards_doubloon).pirate
+ end
+end
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index 278fa63e04..f67d85603a 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -213,10 +213,28 @@ class InheritanceTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::SubclassNotFound) { Company.new(:type => 'Account') }
end
+ def test_new_with_unrelated_namespaced_type
+ without_store_full_sti_class do
+ e = assert_raises ActiveRecord::SubclassNotFound do
+ Namespaced::Company.new(type: 'Firm')
+ end
+
+ assert_equal "Invalid single-table inheritance type: Namespaced::Firm is not a subclass of Namespaced::Company", e.message
+ end
+ end
+
+
def test_new_with_complex_inheritance
assert_nothing_raised { Client.new(type: 'VerySpecialClient') }
end
+ def test_new_without_storing_full_sti_class
+ without_store_full_sti_class do
+ item = Company.new(type: 'SpecialCo')
+ assert_instance_of Company::SpecialCo, item
+ end
+ end
+
def test_new_with_autoload_paths
path = File.expand_path('../../models/autoloadable', __FILE__)
ActiveSupport::Dependencies.autoload_paths << path
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index 46a62c272f..83e50048ec 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -105,7 +105,7 @@ module ActiveRecord
eight = columns.detect { |c| c.name == "eight_int" }
if current_adapter?(:OracleAdapter)
- assert_equal 'NUMBER(8)', eight.sql_type
+ assert_equal 'NUMBER(19)', eight.sql_type
elsif current_adapter?(:SQLite3Adapter)
assert_equal 'bigint', eight.sql_type
else
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 1e93e2a05c..42e7507631 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -897,6 +897,33 @@ class PersistenceTest < ActiveRecord::TestCase
assert_not post.new_record?
end
+ def test_reload_via_querycache
+ ActiveRecord::Base.connection.enable_query_cache!
+ ActiveRecord::Base.connection.clear_query_cache
+ assert ActiveRecord::Base.connection.query_cache_enabled, 'cache should be on'
+ parrot = Parrot.create(:name => 'Shane')
+
+ # populate the cache with the SELECT result
+ found_parrot = Parrot.find(parrot.id)
+ assert_equal parrot.id, found_parrot.id
+
+ # Manually update the 'name' attribute in the DB directly
+ assert_equal 1, ActiveRecord::Base.connection.query_cache.length
+ ActiveRecord::Base.uncached do
+ found_parrot.name = 'Mary'
+ found_parrot.save
+ end
+
+ # Now reload, and verify that it gets the DB version, and not the querycache version
+ found_parrot.reload
+ assert_equal 'Mary', found_parrot.name
+
+ found_parrot = Parrot.find(parrot.id)
+ assert_equal 'Mary', found_parrot.name
+ ensure
+ ActiveRecord::Base.connection.disable_query_cache!
+ end
+
class SaveTest < ActiveRecord::TestCase
self.use_transactional_tests = false
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 83be9a75d8..b8433f0bba 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -175,6 +175,14 @@ class PrimaryKeysTest < ActiveRecord::TestCase
dashboard = Dashboard.first
assert_equal '2', dashboard.id
end
+
+ if current_adapter?(:PostgreSQLAdapter)
+ def test_serial_with_quoted_sequence_name
+ column = MixedCaseMonkey.columns_hash[MixedCaseMonkey.primary_key]
+ assert_equal "nextval('\"mixed_case_monkeys_monkeyID_seq\"'::regclass)", column.default_function
+ assert column.serial?
+ end
+ end
end
class PrimaryKeyWithNoConnectionTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb
index 29c9d0e2af..989f4e1e5d 100644
--- a/activerecord/test/cases/relation/delegation_test.rb
+++ b/activerecord/test/cases/relation/delegation_test.rb
@@ -28,7 +28,7 @@ module ActiveRecord
module DelegationWhitelistBlacklistTests
ARRAY_DELEGATES = [
:+, :-, :|, :&, :[],
- :all?, :collect, :detect, :each, :each_cons, :each_with_index,
+ :all?, :collect, :compact, :detect, :each, :each_cons, :each_with_index,
:exclude?, :find_all, :flat_map, :group_by, :include?, :length,
:map, :none?, :one?, :partition, :reject, :reverse,
:sample, :second, :sort, :sort_by, :third,
diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb
index 45ead08bd5..ba4d9d2503 100644
--- a/activerecord/test/cases/relation/mutation_test.rb
+++ b/activerecord/test/cases/relation/mutation_test.rb
@@ -81,7 +81,7 @@ module ActiveRecord
assert_equal [], relation.extending_values
end
- (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with]).each do |method|
+ (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with, :uniq]).each do |method|
test "##{method}!" do
assert relation.public_send("#{method}!", :foo).equal?(relation)
assert_equal :foo, relation.public_send("#{method}_value")
@@ -153,13 +153,22 @@ module ActiveRecord
test 'distinct!' do
relation.distinct! :foo
assert_equal :foo, relation.distinct_value
- assert_equal :foo, relation.uniq_value # deprecated access
+
+ assert_deprecated do
+ assert_equal :foo, relation.uniq_value # deprecated access
+ end
end
test 'uniq! was replaced by distinct!' do
- relation.uniq! :foo
+ assert_deprecated(/use distinct! instead/) do
+ relation.uniq! :foo
+ end
+
+ assert_deprecated(/use distinct_value instead/) do
+ assert_equal :foo, relation.uniq_value # deprecated access
+ end
+
assert_equal :foo, relation.distinct_value
- assert_equal :foo, relation.uniq_value # deprecated access
end
end
end
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 9353be1ba7..37d3965022 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -242,6 +242,19 @@ module ActiveRecord
assert_equal false, post.respond_to?(:title), "post should not respond_to?(:body) since invoking it raises exception"
end
+ def test_select_quotes_when_using_from_clause
+ if sqlite3_version_includes_quoting_bug?
+ skip <<-ERROR.squish
+ You are using an outdated version of SQLite3 which has a bug in
+ quoted column names. Please update SQLite3 and rebuild the sqlite3
+ ruby gem
+ ERROR
+ end
+ quoted_join = ActiveRecord::Base.connection.quote_table_name("join")
+ selected = Post.select(:join).from(Post.select("id as #{quoted_join}")).map(&:join)
+ assert_equal Post.pluck(:id), selected
+ end
+
def test_relation_merging_with_merged_joins_as_strings
join_string = "LEFT OUTER JOIN #{Rating.quoted_table_name} ON #{SpecialComment.quoted_table_name}.id = #{Rating.quoted_table_name}.comment_id"
special_comments_with_ratings = SpecialComment.joins join_string
@@ -276,5 +289,16 @@ module ActiveRecord
assert_equal "type cast from database", UpdateAllTestModel.first.body
end
+
+ private
+
+ def sqlite3_version_includes_quoting_bug?
+ if current_adapter?(:SQLite3Adapter)
+ selected_quoted_column_names = ActiveRecord::Base.connection.exec_query(
+ 'SELECT "join" FROM (SELECT id AS "join" FROM posts) subquery'
+ ).columns
+ ["join"] != selected_quoted_column_names
+ end
+ end
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index b8e2041b6d..acbf85d398 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -17,7 +17,7 @@ require 'models/tyre'
require 'models/minivan'
require 'models/aircraft'
require "models/possession"
-
+require "models/reader"
class RelationTest < ActiveRecord::TestCase
fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments,
@@ -621,6 +621,32 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 1, query.to_a.size
end
+ def test_preloading_with_associations_and_merges
+ post = Post.create! title: 'Uhuu', body: 'body'
+ reader = Reader.create! post_id: post.id, person_id: 1
+ comment = Comment.create! post_id: post.id, body: 'body'
+
+ assert !comment.respond_to?(:readers)
+
+ post_rel = Post.preload(:readers).joins(:readers).where(title: 'Uhuu')
+ result_comment = Comment.joins(:post).merge(post_rel).to_a.first
+ assert_equal comment, result_comment
+
+ assert_no_queries do
+ assert_equal post, result_comment.post
+ assert_equal [reader], result_comment.post.readers.to_a
+ end
+
+ post_rel = Post.includes(:readers).where(title: 'Uhuu')
+ result_comment = Comment.joins(:post).merge(post_rel).first
+ assert_equal comment, result_comment
+
+ assert_no_queries do
+ assert_equal post, result_comment.post
+ assert_equal [reader], result_comment.post.readers.to_a
+ end
+ end
+
def test_loading_with_one_association
posts = Post.preload(:comments)
post = posts.find { |p| p.id == 1 }
@@ -908,7 +934,7 @@ class RelationTest < ActiveRecord::TestCase
def test_delete_all_with_unpermitted_relation_raises_error
assert_raises(ActiveRecord::ActiveRecordError) { Author.limit(10).delete_all }
- assert_raises(ActiveRecord::ActiveRecordError) { Author.uniq.delete_all }
+ assert_raises(ActiveRecord::ActiveRecordError) { Author.distinct.delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.group(:name).delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.having('SUM(id) < 3').delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.offset(10).delete_all }
@@ -1493,14 +1519,17 @@ class RelationTest < ActiveRecord::TestCase
assert_equal ['Foo', 'Foo'], query.map(&:name)
assert_sql(/DISTINCT/) do
assert_equal ['Foo'], query.distinct.map(&:name)
- assert_equal ['Foo'], query.uniq.map(&:name)
+ assert_deprecated { assert_equal ['Foo'], query.uniq.map(&:name) }
end
assert_sql(/DISTINCT/) do
assert_equal ['Foo'], query.distinct(true).map(&:name)
- assert_equal ['Foo'], query.uniq(true).map(&:name)
+ assert_deprecated { assert_equal ['Foo'], query.uniq(true).map(&:name) }
end
assert_equal ['Foo', 'Foo'], query.distinct(true).distinct(false).map(&:name)
- assert_equal ['Foo', 'Foo'], query.uniq(true).uniq(false).map(&:name)
+
+ assert_deprecated do
+ assert_equal ['Foo', 'Foo'], query.uniq(true).uniq(false).map(&:name)
+ end
end
def test_doesnt_add_having_values_if_options_are_blank
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index e6f0fe6f75..51170c533b 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -253,6 +253,11 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{t\.integer\s+"big_int_data_points\",\s+limit: 8,\s+array: true}, output
end
+ def test_schema_dump_allows_array_of_decimal_defaults
+ output = standard_dump
+ assert_match %r{t\.decimal\s+"decimal_array_default",\s+default: \[1.23, 3.45\],\s+array: true}, output
+ end
+
if ActiveRecord::Base.connection.supports_extensions?
def test_schema_dump_includes_extensions
connection = ActiveRecord::Base.connection
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index 7c92453ee3..a31c3c828c 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -274,4 +274,17 @@ class SerializedAttributeTest < ActiveRecord::TestCase
assert_equal({}, topic.content)
end
+
+ def test_values_cast_from_nil_are_persisted_as_nil
+ # This is required to fulfil the following contract, which must be universally
+ # true in Active Record:
+ #
+ # model.attribute = value
+ # assert_equal model.attribute, model.tap(&:save).reload.attribute
+ Topic.serialize(:content, Hash)
+ topic = Topic.create!(content: {})
+ topic2 = Topic.create!(content: nil)
+
+ assert_equal [topic, topic2], Topic.where(content: nil)
+ end
end
diff --git a/activerecord/test/cases/suppressor_test.rb b/activerecord/test/cases/suppressor_test.rb
index 1c449d42fe..72c5c16555 100644
--- a/activerecord/test/cases/suppressor_test.rb
+++ b/activerecord/test/cases/suppressor_test.rb
@@ -3,7 +3,38 @@ require 'models/notification'
require 'models/user'
class SuppressorTest < ActiveRecord::TestCase
- def test_suppresses_creation_of_record_generated_by_callback
+ def test_suppresses_create
+ assert_no_difference -> { Notification.count } do
+ Notification.suppress do
+ Notification.create
+ Notification.create!
+ Notification.new.save
+ Notification.new.save!
+ end
+ end
+ end
+
+ def test_suppresses_update
+ user = User.create! token: 'asdf'
+
+ User.suppress do
+ user.update token: 'ghjkl'
+ assert_equal 'asdf', user.reload.token
+
+ user.update! token: 'zxcvbnm'
+ assert_equal 'asdf', user.reload.token
+
+ user.token = 'qwerty'
+ user.save
+ assert_equal 'asdf', user.reload.token
+
+ user.token = 'uiop'
+ user.save!
+ assert_equal 'asdf', user.reload.token
+ end
+ end
+
+ def test_suppresses_create_in_callback
assert_difference -> { User.count } do
assert_no_difference -> { Notification.count } do
Notification.suppress { UserWithNotification.create! }
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index f58535f044..d0deb4c273 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -265,14 +265,14 @@ module ActiveRecord
def test_structure_dump
filename = "awesome-file.sql"
- Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "test-db").returns(true)
+ Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "--routines", "test-db").returns(true)
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
end
def test_warn_when_external_structure_dump_fails
filename = "awesome-file.sql"
- Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "test-db").returns(false)
+ Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "--routines", "test-db").returns(false)
warnings = capture(:stderr) do
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
@@ -283,12 +283,21 @@ module ActiveRecord
def test_structure_dump_with_port_number
filename = "awesome-file.sql"
- Kernel.expects(:system).with("mysqldump", "--port", "10000", "--result-file", filename, "--no-data", "test-db").returns(true)
+ Kernel.expects(:system).with("mysqldump", "--port=10000", "--result-file", filename, "--no-data", "--routines", "test-db").returns(true)
ActiveRecord::Tasks::DatabaseTasks.structure_dump(
@configuration.merge('port' => 10000),
filename)
end
+
+ def test_structure_dump_with_ssl
+ filename = "awesome-file.sql"
+ Kernel.expects(:system).with("mysqldump", "--ssl-ca=ca.crt", "--result-file", filename, "--no-data", "--routines", "test-db").returns(true)
+
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(
+ @configuration.merge("sslca" => "ca.crt"),
+ filename)
+ end
end
class MySQLStructureLoadTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
index e0b01ae8e0..a5e6738f14 100644
--- a/activerecord/test/cases/test_case.rb
+++ b/activerecord/test/cases/test_case.rb
@@ -81,7 +81,7 @@ module ActiveRecord
oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im]
mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /]
postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\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]
+ sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im, /^\s*SELECT sql\b.*\bFROM sqlite_master/im]
[oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
ignored_sql.concat db_ignored_sql
diff --git a/activerecord/test/cases/type/integer_test.rb b/activerecord/test/cases/type/integer_test.rb
index 84fb05dd8e..0dcdbd0667 100644
--- a/activerecord/test/cases/type/integer_test.rb
+++ b/activerecord/test/cases/type/integer_test.rb
@@ -21,7 +21,7 @@ module ActiveRecord
type = Type::Integer.new
assert_nil type.cast([1,2])
assert_nil type.cast({1 => 2})
- assert_nil type.cast((1..2))
+ assert_nil type.cast(1..2)
end
test "casting ActiveRecord models" do
diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb
index 3aed90ba36..f9dca1e196 100644
--- a/activerecord/test/cases/view_test.rb
+++ b/activerecord/test/cases/view_test.rb
@@ -102,7 +102,7 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
end
def test_attributes
- assert_equal({"name" => "Agile Web Development with Rails", "status" => 0},
+ assert_equal({"name" => "Agile Web Development with Rails", "status" => 2},
Paperback.first.attributes)
end
diff --git a/activerecord/test/fixtures/books.yml b/activerecord/test/fixtures/books.yml
index abe56752c6..380dd3dfca 100644
--- a/activerecord/test/fixtures/books.yml
+++ b/activerecord/test/fixtures/books.yml
@@ -3,9 +3,20 @@ awdr:
id: 1
name: "Agile Web Development with Rails"
format: "paperback"
+ status: :published
+ read_status: :read
rfr:
author_id: 1
id: 2
name: "Ruby for Rails"
format: "ebook"
+ status: "proposed"
+ read_status: "reading"
+
+ddd:
+ author_id: 1
+ id: 3
+ name: "Domain-Driven Design"
+ format: "hardcover"
+ status: 2
diff --git a/activerecord/test/fixtures/doubloons.yml b/activerecord/test/fixtures/doubloons.yml
new file mode 100644
index 0000000000..efd1643971
--- /dev/null
+++ b/activerecord/test/fixtures/doubloons.yml
@@ -0,0 +1,3 @@
+blackbeards_doubloon:
+ pirate: blackbeard
+ weight: 2
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index 6961f8fd6f..67936e8e5d 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -26,6 +26,9 @@ class Company < AbstractCompany
def private_method
"I am Jack's innermost fears and aspirations"
end
+
+ class SpecialCo < Company
+ end
end
module Namespaced
diff --git a/activerecord/test/models/doubloon.rb b/activerecord/test/models/doubloon.rb
new file mode 100644
index 0000000000..2b11d128e2
--- /dev/null
+++ b/activerecord/test/models/doubloon.rb
@@ -0,0 +1,12 @@
+class AbstractDoubloon < ActiveRecord::Base
+ # This has functionality that might be shared by multiple classes.
+
+ self.abstract_class = true
+ belongs_to :pirate
+end
+
+class Doubloon < AbstractDoubloon
+ # This uses an abstract class that defines attributes and associations.
+
+ self.table_name = 'doubloons'
+end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 872fa595b4..df0362573b 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -107,5 +107,6 @@ _SQL
create_table :bigint_array, force: true do |t|
t.integer :big_int_data_points, limit: 8, array: true
+ t.decimal :decimal_array_default, array: true, default: [1.23, 3.45]
end
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 66f8f1611d..8b010f265c 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -266,6 +266,11 @@ ActiveRecord::Schema.define do
t.string :alias
end
+ create_table :doubloons, force: true do |t|
+ t.integer :pirate_id
+ t.integer :weight
+ end
+
create_table :edges, force: true, id: false do |t|
t.column :source_id, :integer, null: false
t.column :sink_id, :integer, null: false
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index ac27dc640e..6ebbdbc3db 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,4 +1,50 @@
-* Encoding ActiveSupport::TimeWithZone to YAML now preserves the timezone information.
+* Fix a range of values for parameters of the Time#change
+
+ *Nikolay Kondratyev*
+
+* Add `Enumerable#pluck` to get the same values from arrays as from ActiveRecord
+ associations.
+
+ Fixes #20339.
+
+ *Kevin Deisz*
+
+* Add a bang version to `ActiveSupport::OrderedOptions` get methods which will raise
+ an `KeyError` if the value is `.blank?`
+
+ Before:
+
+ if (slack_url = Rails.application.secrets.slack_url).present?
+ # Do something worthwhile
+ else
+ # Raise as important secret password is not specified
+ end
+
+ After:
+
+ slack_url = Rails.application.secrets.slack_url!
+
+ *Aditya Sanghi*, *Gaurish Sharma*
+
+* Remove deprecated `Class#superclass_delegating_accessor`.
+ Use `Class#class_attribute` instead.
+
+ *Akshay Vishnoi*
+
+* Patch `Delegator` to work with `#try`.
+
+ Fixes #5790.
+
+ *Nate Smith*
+
+* Add `Integer#positive?` and `Integer#negative?` query methods
+ in the vein of `Fixnum#zero?`.
+
+ This makes it nicer to do things like `bunch_of_numbers.select(&:positive?)`.
+
+ *DHH*
+
+* Encoding `ActiveSupport::TimeWithZone` to YAML now preserves the timezone information.
Fixes #9183.
@@ -44,12 +90,12 @@
*Todd Bealmear*
-* Fixed a problem where String#truncate_words would get stuck with a complex
+* Fixed a problem where `String#truncate_words` would get stuck with a complex
string.
*Henrik Nygren*
-* Fixed a roundtrip problem with AS::SafeBuffer where primitive-like strings
+* Fixed a roundtrip problem with `AS::SafeBuffer` where primitive-like strings
will be dumped as primitives:
Before:
@@ -96,7 +142,7 @@
*Ian Ker-Seymer*
-* Duplicate frozen array when assigning it to a HashWithIndifferentAccess so
+* Duplicate frozen array when assigning it to a `HashWithIndifferentAccess` so
that it doesn't raise a `RuntimeError` when calling `map!` on it in `convert_value`.
Fixes #18550.
@@ -125,7 +171,7 @@
* Add `#on_weekend?`, `#next_weekday`, `#prev_weekday` methods to `Date`,
`Time`, and `DateTime`.
- `#on_weekend?` returns true if the receiving date/time falls on a Saturday
+ `#on_weekend?` returns `true` if the receiving date/time falls on a Saturday
or Sunday.
`#next_weekday` returns a new date/time representing the next day that does
@@ -179,13 +225,13 @@
`Callbacks::CallbackChain.halt_and_display_warning_on_return_false`, will
either not work at all or display a deprecation warning.
-* Add Callbacks::CallbackChain.halt_and_display_warning_on_return_false
+* Add `Callbacks::CallbackChain.halt_and_display_warning_on_return_false`
Setting `Callbacks::CallbackChain.halt_and_display_warning_on_return_false`
- to true will let an app support the deprecated way of halting callback
+ to `true` will let an app support the deprecated way of halting callback
chains by returning `false`.
- Setting the value to false will tell the app to ignore any `false` value
+ Setting the value to `false` will tell the app to ignore any `false` value
returned by callbacks, and only halt the chain upon `throw(:abort)`.
The value can also be set with the Rails configuration option
@@ -198,7 +244,7 @@
*claudiob*
-* Changes arguments and default value of CallbackChain's :terminator option
+* Changes arguments and default value of CallbackChain's `:terminator` option
Chains of callbacks defined without an explicit `:terminator` option will
now be halted as soon as a `before_` callback throws `:abort`.
diff --git a/activesupport/README.rdoc b/activesupport/README.rdoc
index a6424a353a..cd72f53821 100644
--- a/activesupport/README.rdoc
+++ b/activesupport/README.rdoc
@@ -10,7 +10,7 @@ outside of Rails.
The latest version of Active Support can be installed with RubyGems:
- % [sudo] gem install activesupport
+ % gem install activesupport
Source code can be downloaded as part of the Rails project on GitHub:
diff --git a/activesupport/Rakefile b/activesupport/Rakefile
index 7c40df8dc8..81c242d4b1 100644
--- a/activesupport/Rakefile
+++ b/activesupport/Rakefile
@@ -1,5 +1,4 @@
require 'rake/testtask'
-require 'rubygems/package_task'
task :default => :test
Rake::TestTask.new do |t|
@@ -17,16 +16,3 @@ namespace :test do
end or raise "Failures"
end
end
-
-spec = eval(File.read('activesupport.gemspec'))
-
-Gem::PackageTask.new(spec) do |p|
- p.gem_spec = spec
-end
-
-desc "Release to rubygems"
-task :release => :package do
- require 'rake/gemcutter'
- Rake::Gemcutter::Tasks.new(spec).define
- Rake::Task['gem:push'].invoke
-end
diff --git a/activesupport/lib/active_support/array_inquirer.rb b/activesupport/lib/active_support/array_inquirer.rb
index 0ae534da00..e7188d7adb 100644
--- a/activesupport/lib/active_support/array_inquirer.rb
+++ b/activesupport/lib/active_support/array_inquirer.rb
@@ -7,11 +7,17 @@ module ActiveSupport
# variants.phone? # => true
# variants.tablet? # => true
# variants.desktop? # => false
- #
- # variants.any?(:phone, :tablet) # => true
- # variants.any?(:phone, :desktop) # => true
- # variants.any?(:desktop, :watch) # => false
class ArrayInquirer < Array
+ # Passes each element of +candidates+ collection to ArrayInquirer collection.
+ # The method returns true if at least one element is the same. If +candidates+
+ # collection is not given, method returns true.
+ #
+ # variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
+ #
+ # variants.any? # => true
+ # variants.any?(:phone, :tablet) # => true
+ # variants.any?('phone', 'desktop') # => true
+ # variants.any?(:desktop, :watch) # => false
def any?(*candidates, &block)
if candidates.none?
super
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index d06f22ad5c..e161ec4cca 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -25,7 +25,7 @@ module ActiveSupport
# of the backtrace, you can call <tt>BacktraceCleaner#remove_filters!</tt>
# These two methods will give you a completely untouched backtrace.
#
- # Inspired by the Quiet Backtrace gem by Thoughtbot.
+ # Inspired by the Quiet Backtrace gem by thoughtbot.
class BacktraceCleaner
def initialize
@filters, @silencers = [], []
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 73ae3acea5..47133bf550 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -26,7 +26,14 @@ module ActiveSupport
class MemCacheStore < Store
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
- def self.build_mem_cache(*addresses)
+ # Creates a new Dalli::Client instance with specified addresses and options.
+ # By default address is equal localhost:11211.
+ #
+ # ActiveSupport::Cache::MemCacheStore.build_mem_cache
+ # # => #<Dalli::Client:0x007f98a47d2028 @servers=["localhost:11211"], @options={}, @ring=nil>
+ # ActiveSupport::Cache::MemCacheStore.build_mem_cache('localhost:10290')
+ # # => #<Dalli::Client:0x007f98a47b3a60 @servers=["localhost:10290"], @options={}, @ring=nil>
+ def self.build_mem_cache(*addresses) # :nodoc:
addresses = addresses.flatten
options = addresses.extract_options!
addresses = ["localhost:11211"] if addresses.empty?
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index 8a0523d0e2..90bb2c38c3 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -126,7 +126,7 @@ module ActiveSupport
PER_ENTRY_OVERHEAD = 240
- def cached_size(key, entry)
+ def cached_size(key, entry) # :nodoc:
key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD
end
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index a913736fc3..fe5bc82c30 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -120,7 +120,7 @@ module ActiveSupport
super
end
- def set_cache_value(value, name, amount, options)
+ def set_cache_value(value, name, amount, options) # :nodoc:
if local_cache
local_cache.mute do
if value
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 814fd288cf..e8ab3a7db5 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -638,7 +638,7 @@ module ActiveSupport
# set_callback :save, :after, :after_meth, if: :condition
# set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
#
- # The second arguments indicates whether the callback is to be run +:before+,
+ # The second argument indicates whether the callback is to be run +:before+,
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
# means the first example above can also be written as:
#
diff --git a/activesupport/lib/active_support/core_ext/class.rb b/activesupport/lib/active_support/core_ext/class.rb
index c750a10bb2..ef903d59b5 100644
--- a/activesupport/lib/active_support/core_ext/class.rb
+++ b/activesupport/lib/active_support/core_ext/class.rb
@@ -1,3 +1,2 @@
require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/class/delegating_attributes'
require 'active_support/core_ext/class/subclasses'
diff --git a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
deleted file mode 100644
index 1c305c5970..0000000000
--- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/core_ext/module/remove_method'
-require 'active_support/core_ext/module/deprecation'
-
-
-class Class
- def superclass_delegating_accessor(name, options = {})
- # Create private _name and _name= methods that can still be used if the public
- # methods are overridden.
- _superclass_delegating_accessor("_#{name}", options)
-
- # Generate the public methods name, name=, and name?.
- # These methods dispatch to the private _name, and _name= methods, making them
- # overridable.
- singleton_class.send(:define_method, name) { send("_#{name}") }
- singleton_class.send(:define_method, "#{name}?") { !!send("_#{name}") }
- singleton_class.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) }
-
- # If an instance_reader is needed, generate public instance methods name and name?.
- if options[:instance_reader] != false
- define_method(name) { send("_#{name}") }
- define_method("#{name}?") { !!send("#{name}") }
- end
- end
-
- deprecate superclass_delegating_accessor: :class_attribute
-
- private
- # Take the object being set and store it in a method. This gives us automatic
- # inheritance behavior, without having to store the object in an instance
- # variable and look up the superclass chain manually.
- def _stash_object_in_method(object, method, instance_reader = true)
- singleton_class.remove_possible_method(method)
- singleton_class.send(:define_method, method) { object }
- remove_possible_method(method)
- define_method(method) { object } if instance_reader
- end
-
- def _superclass_delegating_accessor(name, options = {})
- singleton_class.send(:define_method, "#{name}=") do |value|
- _stash_object_in_method(value, name, options[:instance_reader] != false)
- end
- send("#{name}=", nil)
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb
index df419a6e63..31479a1269 100644
--- a/activesupport/lib/active_support/core_ext/date/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date/conversions.rb
@@ -35,6 +35,7 @@ class Date
# date.to_s(:db) # => "2007-11-10"
#
# date.to_formatted_s(:short) # => "10 Nov"
+ # date.to_formatted_s(:number) # => "20071110"
# date.to_formatted_s(:long) # => "November 10, 2007"
# date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
# date.to_formatted_s(:rfc822) # => "10 Nov 2007"
@@ -82,6 +83,11 @@ class Date
::Time.send(form, year, month, day)
end
+ # Returns a string which represents the time in used time zone as DateTime
+ # defined by XML Schema:
+ #
+ # date = Date.new(2015, 05, 23) # => Sat, 23 May 2015
+ # date.xmlschema # => "2015-05-23T00:00:00+04:00"
def xmlschema
in_time_zone.xmlschema
end
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index 7a893292b3..fc7531d088 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -71,6 +71,21 @@ module Enumerable
def without(*elements)
reject { |element| elements.include?(element) }
end
+
+ # Convert an enumerable to an array based on the given key.
+ #
+ # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
+ # => ["David", "Rafael", "Aaron"]
+ #
+ # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
+ # => [[1, "David"], [2, "Rafael"]]
+ def pluck(*keys)
+ if keys.many?
+ map { |element| keys.map { |key| element[key] } }
+ else
+ map { |element| element[keys.first] }
+ end
+ end
end
class Range #:nodoc:
diff --git a/activesupport/lib/active_support/core_ext/numeric.rb b/activesupport/lib/active_support/core_ext/numeric.rb
index a6bc0624be..bcdc3eace2 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/inquiry'
require 'active_support/core_ext/numeric/conversions'
diff --git a/activesupport/lib/active_support/core_ext/numeric/inquiry.rb b/activesupport/lib/active_support/core_ext/numeric/inquiry.rb
new file mode 100644
index 0000000000..7e7ac1b0b2
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/numeric/inquiry.rb
@@ -0,0 +1,26 @@
+unless 1.respond_to?(:positive?) # TODO: Remove this file when we drop support to ruby < 2.3
+class Numeric
+ # Returns true if the number is positive.
+ #
+ # 1.positive? # => true
+ # 0.positive? # => false
+ # -1.positive? # => false
+ def positive?
+ self > 0
+ end
+
+ # Returns true if the number is negative.
+ #
+ # -1.negative? # => true
+ # 0.negative? # => false
+ # 1.negative? # => false
+ def negative?
+ self < 0
+ end
+end
+
+class Complex
+ undef :positive?
+ undef :negative?
+end
+end
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index e0f70b9caa..69be6c4abc 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -1,4 +1,34 @@
+require 'delegate'
+
+module ActiveSupport
+ module Tryable #:nodoc:
+ def try(*a, &b)
+ try!(*a, &b) if a.empty? || respond_to?(a.first)
+ end
+
+ def try!(*a, &b)
+ if a.empty? && block_given?
+ if b.arity.zero?
+ instance_eval(&b)
+ else
+ yield self
+ end
+ else
+ public_send(*a, &b)
+ end
+ end
+ end
+end
+
class Object
+ include ActiveSupport::Tryable
+
+ ##
+ # :method: try
+ #
+ # :call-seq:
+ # try(*a, &b)
+ #
# Invokes the public method whose name goes as first argument just like
# +public_send+ does, except that if the receiver does not respond to it the
# call returns +nil+ rather than raising an exception.
@@ -56,30 +86,40 @@ class Object
#
# Please also note that +try+ is defined on +Object+. Therefore, it won't work
# with instances of classes that do not have +Object+ among their ancestors,
- # like direct subclasses of +BasicObject+. For example, using +try+ with
- # +SimpleDelegator+ will delegate +try+ to the target instead of calling it on
- # the delegator itself.
- def try(*a, &b)
- try!(*a, &b) if a.empty? || respond_to?(a.first)
- end
+ # like direct subclasses of +BasicObject+.
+ ##
+ # :method: try!
+ #
+ # :call-seq:
+ # try!(*a, &b)
+ #
# Same as #try, but raises a NoMethodError exception if the receiver is
# not +nil+ and does not implement the tried method.
#
# "a".try!(:upcase) # => "A"
# nil.try!(:upcase) # => nil
# 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Fixnum
- def try!(*a, &b)
- if a.empty? && block_given?
- if b.arity.zero?
- instance_eval(&b)
- else
- yield self
- end
- else
- public_send(*a, &b)
- end
- end
+end
+
+class Delegator
+ include ActiveSupport::Tryable
+
+ ##
+ # :method: try
+ #
+ # :call-seq:
+ # try(a*, &b)
+ #
+ # See Object#try
+
+ ##
+ # :method: try!
+ #
+ # :call-seq:
+ # try!(a*, &b)
+ #
+ # See Object#try!
end
class NilClass
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 1ce68ea7c7..c554501893 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -98,7 +98,7 @@ class Time
elsif zone
::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
else
- raise ArgumentError, 'argument out of range' if new_usec > 999999
+ raise ArgumentError, 'argument out of range' if new_usec >= 1000000
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset)
end
end
diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb
index bfe0832b37..0b2ff817c3 100644
--- a/activesupport/lib/active_support/core_ext/uri.rb
+++ b/activesupport/lib/active_support/core_ext/uri.rb
@@ -12,7 +12,7 @@ unless str == parser.unescape(parser.escape(str))
# YK: My initial experiments say yes, but let's be sure please
enc = str.encoding
enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
- str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
+ str.gsub(escaped) { |match| [match[1, 2].hex].pack('C') }.force_encoding(enc)
end
end
end
diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb
index 9f9dca8453..28d2d78643 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -38,6 +38,18 @@ module ActiveSupport
silence: ->(message, callstack) {},
}
+ # Behavior module allows to determine how to display deprecation messages.
+ # You can create a custom behavior or set any from the +DEFAULT_BEHAVIORS+
+ # constant. Available behaviors are:
+ #
+ # [+raise+] Raise <tt>ActiveSupport::DeprecationException</tt>.
+ # [+stderr+] Log all deprecation warnings to +$stderr+.
+ # [+log+] Log all deprecation warnings to +Rails.logger+.
+ # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
+ # [+silence+] Do nothing.
+ #
+ # Setting behaviors only affects deprecations that happen after boot time.
+ # For more information you can read the documentation of the +behavior=+ method.
module Behavior
# Whether to print a backtrace along with the warning.
attr_accessor :debug
diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
index dfdb8034e5..9b264cbb79 100644
--- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
@@ -104,7 +104,7 @@ module ActiveSupport
end
# DeprecatedConstantProxy transforms a constant into a deprecated one. It
- # takes the names of an old (deprecated) constant and of a new contstant
+ # takes the names of an old (deprecated) constant and of a new constant
# (both in string form) and optionally a deprecator. The deprecator defaults
# to +ActiveSupport::Deprecator+ if none is specified. The deprecated constant
# now returns the value of the new one.
@@ -127,6 +127,11 @@ module ActiveSupport
@deprecator = deprecator
end
+ # Returns the class of the new constant.
+ #
+ # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
+ # PLANETS = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('PLANETS', 'PLANETS_POST_2006')
+ # PLANETS.class # => Array
def class
target.class
end
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 4c0d1197fe..c63b61e97a 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -52,6 +52,10 @@ module ActiveSupport
end
end
+ # Returns the amount of seconds a duration covers as a string.
+ # For more information check to_i method.
+ #
+ # 1.day.to_s # => "86400"
def to_s
@value.to_s
end
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index a08c655d69..be369d21c6 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -68,9 +68,9 @@ module ActiveSupport
def camelize(term, uppercase_first_letter = true)
string = term.to_s
if uppercase_first_letter
- string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
+ string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
else
- string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
+ string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
end
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
string.gsub!('/'.freeze, '::'.freeze)
@@ -153,7 +153,7 @@ module ActiveSupport
# titleize('TheManWithoutAPast') # => "The Man Without A Past"
# titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
def titleize(word)
- humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
+ humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { |match| match.capitalize }
end
# Creates the name of a table like Rails does for models to table names.
@@ -231,8 +231,8 @@ module ActiveSupport
# Tries to find a constant with the name specified in the argument string.
#
- # 'Module'.constantize # => Module
- # 'Test::Unit'.constantize # => Test::Unit
+ # 'Module'.constantize # => Module
+ # 'Foo::Bar'.constantize # => Foo::Bar
#
# The name is assumed to be the one of a top-level constant, no matter
# whether it starts with "::" or not. No lexical context is taken into
@@ -280,8 +280,8 @@ module ActiveSupport
# Tries to find a constant with the name specified in the argument string.
#
- # safe_constantize('Module') # => Module
- # safe_constantize('Test::Unit') # => Test::Unit
+ # safe_constantize('Module') # => Module
+ # safe_constantize('Foo::Bar') # => Foo::Bar
#
# The name is assumed to be the one of a top-level constant, no matter
# whether it starts with "::" or not. No lexical context is taken into
diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb
index 35548f3f56..2932954f03 100644
--- a/activesupport/lib/active_support/json/decoding.rb
+++ b/activesupport/lib/active_support/json/decoding.rb
@@ -9,20 +9,14 @@ module ActiveSupport
module JSON
# matches YAML-formatted dates
DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
-
+
class << self
# Parses a JSON string (JavaScript Object Notation) into a hash.
# See http://www.json.org for more info.
#
# ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
# => {"team" => "rails", "players" => "36"}
- def decode(json, options = {})
- if options.present?
- raise ArgumentError, "In Rails 4.1, ActiveSupport::JSON.decode no longer " \
- "accepts an options hash for MultiJSON. MultiJSON reached its end of life " \
- "and has been removed."
- end
-
+ def decode(json)
data = ::JSON.parse(json, quirks_mode: true)
if ActiveSupport.parse_json_times
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index 3c0cf9f137..45cf6fc1ef 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -86,8 +86,14 @@ module ActiveSupport #:nodoc:
@wrapped_string.split(*args).map { |i| self.class.new(i) }
end
- # Works like like <tt>String#slice!</tt>, but returns an instance of
+ # Works like <tt>String#slice!</tt>, but returns an instance of
# Chars, or nil if the string was not modified.
+ #
+ # string = 'Welcome'
+ # string.mb_chars.slice!(3) # => #<ActiveSupport::Multibyte::Chars:0x000000038109b8 @wrapped_string="c">
+ # string # => 'Welome'
+ # string.mb_chars.slice!(0..3) # => #<ActiveSupport::Multibyte::Chars:0x00000002eb80a0 @wrapped_string="Welo">
+ # string # => 'me'
def slice!(*args)
chars(@wrapped_string.slice!(*args))
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
index cd5a2b3cbb..7986eb50f0 100644
--- a/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_currency_converter.rb
@@ -23,7 +23,7 @@ module ActiveSupport
end
def absolute_value(number)
- number.respond_to?("abs") ? number.abs : number.sub(/\A-/, '')
+ number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, '')
end
def options
diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb
index a33e2c58a9..bc0326473d 100644
--- a/activesupport/lib/active_support/ordered_options.rb
+++ b/activesupport/lib/active_support/ordered_options.rb
@@ -31,7 +31,13 @@ module ActiveSupport
if name_string.chomp!('=')
self[name_string] = args.first
else
- self[name]
+ bangs = name_string.chomp!('!')
+
+ if bangs
+ fetch(name_string.to_sym).presence || raise(KeyError.new("#{name_string} is blank."))
+ else
+ self[name_string]
+ end
end
end
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index d9a668c0ea..ae6f00b861 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -9,6 +9,7 @@ require 'active_support/testing/isolation'
require 'active_support/testing/constant_lookup'
require 'active_support/testing/time_helpers'
require 'active_support/testing/file_fixtures'
+require 'active_support/testing/composite_filter'
require 'active_support/core_ext/kernel/reporting'
module ActiveSupport
@@ -38,6 +39,15 @@ module ActiveSupport
def test_order
ActiveSupport.test_order ||= :random
end
+
+ def run(reporter, options = {})
+ if options[:patterns] && options[:patterns].any? { |p| p =~ /:\d+/ }
+ options[:filter] = \
+ Testing::CompositeFilter.new(self, options[:filter], options[:patterns])
+ end
+
+ super
+ end
end
alias_method :method_name, :name
diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb
index 8b649c193f..d87ce3474d 100644
--- a/activesupport/lib/active_support/testing/assertions.rb
+++ b/activesupport/lib/active_support/testing/assertions.rb
@@ -23,42 +23,42 @@ module ActiveSupport
# result of what is evaluated in the yielded block.
#
# assert_difference 'Article.count' do
- # post :create, article: {...}
+ # post :create, params: { article: {...} }
# end
#
# An arbitrary expression is passed in and evaluated.
#
- # assert_difference 'assigns(:article).comments(:reload).size' do
- # post :create, comment: {...}
+ # assert_difference 'Article.last.comments(:reload).size' do
+ # post :create, params: { comment: {...} }
# end
#
# An arbitrary positive or negative difference can be specified.
# The default is <tt>1</tt>.
#
# assert_difference 'Article.count', -1 do
- # post :delete, id: ...
+ # post :delete, params: { id: ... }
# end
#
# An array of expressions can also be passed in and evaluated.
#
# assert_difference [ 'Article.count', 'Post.count' ], 2 do
- # post :create, article: {...}
+ # post :create, params: { article: {...} }
# end
#
# A lambda or a list of lambdas can be passed in and evaluated:
#
# assert_difference ->{ Article.count }, 2 do
- # post :create, article: {...}
+ # post :create, params: { article: {...} }
# end
#
# assert_difference [->{ Article.count }, ->{ Post.count }], 2 do
- # post :create, article: {...}
+ # post :create, params: { article: {...} }
# end
#
# An error message can be specified.
#
# assert_difference 'Article.count', -1, 'An Article should be destroyed' do
- # post :delete, id: ...
+ # post :delete, params: { id: ... }
# end
def assert_difference(expression, difference = 1, message = nil, &block)
expressions = Array(expression)
@@ -81,13 +81,13 @@ module ActiveSupport
# changed before and after invoking the passed in block.
#
# assert_no_difference 'Article.count' do
- # post :create, article: invalid_attributes
+ # post :create, params: { article: invalid_attributes }
# end
#
# An error message can be specified.
#
# assert_no_difference 'Article.count', 'An Article should not be created' do
- # post :create, article: invalid_attributes
+ # post :create, params: { article: invalid_attributes }
# end
def assert_no_difference(expression, message = nil, &block)
assert_difference expression, 0, message, &block
diff --git a/activesupport/lib/active_support/testing/composite_filter.rb b/activesupport/lib/active_support/testing/composite_filter.rb
new file mode 100644
index 0000000000..bde723e30b
--- /dev/null
+++ b/activesupport/lib/active_support/testing/composite_filter.rb
@@ -0,0 +1,54 @@
+require 'method_source'
+
+module ActiveSupport
+ module Testing
+ class CompositeFilter # :nodoc:
+ def initialize(runnable, filter, patterns)
+ @runnable = runnable
+ @filters = [ derive_regexp(filter), *derive_line_filters(patterns) ].compact
+ end
+
+ def ===(method)
+ @filters.any? { |filter| filter === method }
+ end
+
+ private
+ def derive_regexp(filter)
+ filter =~ %r%/(.*)/% ? Regexp.new($1) : filter
+ end
+
+ def derive_line_filters(patterns)
+ patterns.map do |file_and_line|
+ file, line = file_and_line.split(':')
+ Filter.new(@runnable, file, line) if file
+ end
+ end
+
+ class Filter # :nodoc:
+ def initialize(runnable, file, line)
+ @runnable, @file = runnable, File.expand_path(file)
+ @line = line.to_i if line
+ end
+
+ def ===(method)
+ return unless @runnable.method_defined?(method)
+
+ if @line
+ test_file, test_range = definition_for(@runnable.instance_method(method))
+ test_file == @file && test_range.include?(@line)
+ else
+ @runnable.instance_method(method).source_location.first == @file
+ end
+ end
+
+ private
+ def definition_for(method)
+ file, start_line = method.source_location
+ end_line = method.source.count("\n") + start_line - 1
+
+ return file, start_line..end_line
+ end
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/testing/time_helpers.rb b/activesupport/lib/active_support/testing/time_helpers.rb
index 3478b09423..fca0947c5b 100644
--- a/activesupport/lib/active_support/testing/time_helpers.rb
+++ b/activesupport/lib/active_support/testing/time_helpers.rb
@@ -39,7 +39,7 @@ module ActiveSupport
end
end
- # Contain helpers that help you test passage of time.
+ # Contains helpers that help you test passage of time.
module TimeHelpers
# Changes current time to the time in the future or in the past by a given time difference by
# stubbing +Time.now+, +Date.today+, and +DateTime.now+.
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index b0d7f3299f..412c72d27c 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -1,3 +1,4 @@
+require 'active_support/duration'
require 'active_support/values/time_zone'
require 'active_support/core_ext/object/acts_like'
diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb
index 7ffcae6007..9b88a2dcc1 100644
--- a/activesupport/test/abstract_unit.rb
+++ b/activesupport/test/abstract_unit.rb
@@ -37,4 +37,5 @@ def jruby_skip(message = '')
skip message if defined?(JRUBY_VERSION)
end
+require 'minitest/mock'
require 'mocha/setup' # FIXME: stop using mocha
diff --git a/activesupport/test/autoloading_fixtures/a/c/e/f.rb b/activesupport/test/autoloading_fixtures/a/c/e/f.rb
deleted file mode 100644
index 57dba5a307..0000000000
--- a/activesupport/test/autoloading_fixtures/a/c/e/f.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class A::C::E::F
-end \ No newline at end of file
diff --git a/activesupport/test/autoloading_fixtures/a/c/em/f.rb b/activesupport/test/autoloading_fixtures/a/c/em/f.rb
new file mode 100644
index 0000000000..8b28e19148
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/a/c/em/f.rb
@@ -0,0 +1,2 @@
+class A::C::EM::F
+end \ No newline at end of file
diff --git a/activesupport/test/autoloading_fixtures/d.rb b/activesupport/test/autoloading_fixtures/d.rb
new file mode 100644
index 0000000000..45c794d4ca
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/d.rb
@@ -0,0 +1,2 @@
+class D
+end \ No newline at end of file
diff --git a/activesupport/test/autoloading_fixtures/e.rb b/activesupport/test/autoloading_fixtures/e.rb
deleted file mode 100644
index 2f59e4fb75..0000000000
--- a/activesupport/test/autoloading_fixtures/e.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class E
-end \ No newline at end of file
diff --git a/activesupport/test/autoloading_fixtures/em.rb b/activesupport/test/autoloading_fixtures/em.rb
new file mode 100644
index 0000000000..16a1838667
--- /dev/null
+++ b/activesupport/test/autoloading_fixtures/em.rb
@@ -0,0 +1,2 @@
+class EM
+end \ No newline at end of file
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 527538ed9a..8e22d4ed2f 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -288,8 +288,9 @@ module CacheStoreBehavior
@cache.write('foo', 'bar', :expires_in => 10)
@cache.write('fu', 'baz')
@cache.write('fud', 'biz')
- Time.stubs(:now).returns(time + 11)
- assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
+ Time.stub(:now, time + 11) do
+ assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
+ end
end
def test_fetch_multi
@@ -387,66 +388,74 @@ module CacheStoreBehavior
def test_expires_in
time = Time.local(2008, 4, 24)
- Time.stubs(:now).returns(time)
- @cache.write('foo', 'bar')
- assert_equal 'bar', @cache.read('foo')
+ Time.stub(:now, time) do
+ @cache.write('foo', 'bar')
+ assert_equal 'bar', @cache.read('foo')
+ end
- Time.stubs(:now).returns(time + 30)
- assert_equal 'bar', @cache.read('foo')
+ Time.stub(:now, time + 30) do
+ assert_equal 'bar', @cache.read('foo')
+ end
- Time.stubs(:now).returns(time + 61)
- assert_nil @cache.read('foo')
+ Time.stub(:now, time + 61) do
+ assert_nil @cache.read('foo')
+ end
end
def test_race_condition_protection_skipped_if_not_defined
@cache.write('foo', 'bar')
time = @cache.send(:read_entry, 'foo', {}).expires_at
- Time.stubs(:now).returns(Time.at(time))
- result = @cache.fetch('foo') do
- assert_equal nil, @cache.read('foo')
- 'baz'
+ Time.stub(:now, Time.at(time)) do
+ result = @cache.fetch('foo') do
+ assert_equal nil, @cache.read('foo')
+ 'baz'
+ end
+ assert_equal 'baz', result
end
- assert_equal 'baz', result
end
def test_race_condition_protection_is_limited
time = Time.now
@cache.write('foo', 'bar', :expires_in => 60)
- Time.stubs(:now).returns(time + 71)
- result = @cache.fetch('foo', :race_condition_ttl => 10) do
- assert_equal nil, @cache.read('foo')
- "baz"
+ Time.stub(:now, time + 71) do
+ result = @cache.fetch('foo', :race_condition_ttl => 10) do
+ assert_equal nil, @cache.read('foo')
+ "baz"
+ end
+ assert_equal "baz", result
end
- assert_equal "baz", result
end
def test_race_condition_protection_is_safe
time = Time.now
@cache.write('foo', 'bar', :expires_in => 60)
- Time.stubs(:now).returns(time + 61)
- begin
- @cache.fetch('foo', :race_condition_ttl => 10) do
- assert_equal 'bar', @cache.read('foo')
- raise ArgumentError.new
+ Time.stub(:now, time + 61) do
+ begin
+ @cache.fetch('foo', :race_condition_ttl => 10) do
+ assert_equal 'bar', @cache.read('foo')
+ raise ArgumentError.new
+ end
+ rescue ArgumentError
end
- rescue ArgumentError
+ assert_equal "bar", @cache.read('foo')
+ end
+ Time.stub(:now, time + 91) do
+ assert_nil @cache.read('foo')
end
- assert_equal "bar", @cache.read('foo')
- Time.stubs(:now).returns(time + 91)
- assert_nil @cache.read('foo')
end
def test_race_condition_protection
time = Time.now
@cache.write('foo', 'bar', :expires_in => 60)
- Time.stubs(:now).returns(time + 61)
- result = @cache.fetch('foo', :race_condition_ttl => 10) do
- assert_equal 'bar', @cache.read('foo')
- "baz"
+ Time.stub(:now, time + 61) do
+ result = @cache.fetch('foo', :race_condition_ttl => 10) do
+ assert_equal 'bar', @cache.read('foo')
+ "baz"
+ end
+ assert_equal "baz", result
end
- assert_equal "baz", result
end
def test_crazy_key_characters
@@ -636,37 +645,37 @@ module AutoloadingCacheBehavior
include DependenciesTestHelpers
def test_simple_autoloading
with_autoloading_fixtures do
- @cache.write('foo', E.new)
+ @cache.write('foo', EM.new)
end
- remove_constants(:E)
+ remove_constants(:EM)
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
- assert_kind_of E, @cache.read('foo')
+ assert_kind_of EM, @cache.read('foo')
end
- remove_constants(:E)
+ remove_constants(:EM)
ActiveSupport::Dependencies.clear
end
def test_two_classes_autoloading
with_autoloading_fixtures do
- @cache.write('foo', [E.new, ClassFolder.new])
+ @cache.write('foo', [EM.new, ClassFolder.new])
end
- remove_constants(:E, :ClassFolder)
+ remove_constants(:EM, :ClassFolder)
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
loaded = @cache.read('foo')
assert_kind_of Array, loaded
assert_equal 2, loaded.size
- assert_kind_of E, loaded[0]
+ assert_kind_of EM, loaded[0]
assert_kind_of ClassFolder, loaded[1]
end
- remove_constants(:E, :ClassFolder)
+ remove_constants(:EM, :ClassFolder)
ActiveSupport::Dependencies.clear
end
end
@@ -775,11 +784,12 @@ class FileStoreTest < ActiveSupport::TestCase
@cache.write('foo', 'bar', expires_in: 10)
@cache.write('baz', 'qux')
@cache.write('quux', 'corge', expires_in: 20)
- Time.stubs(:now).returns(time + 15)
- @cache.cleanup
- assert_not @cache.exist?('foo')
- assert @cache.exist?('baz')
- assert @cache.exist?('quux')
+ Time.stub(:now, time + 15) do
+ @cache.cleanup
+ assert_not @cache.exist?('foo')
+ assert @cache.exist?('baz')
+ assert @cache.exist?('quux')
+ end
end
def test_write_with_unless_exist
@@ -1056,9 +1066,9 @@ class CacheEntryTest < ActiveSupport::TestCase
assert !entry.expired?, 'entry not expired'
entry = ActiveSupport::Cache::Entry.new("value", :expires_in => 60)
assert !entry.expired?, 'entry not expired'
- time = Time.now + 61
- Time.stubs(:now).returns(time)
- assert entry.expired?, 'entry is expired'
+ Time.stub(:now, Time.now + 61) do
+ assert entry.expired?, 'entry is expired'
+ end
end
def test_compress_values
diff --git a/activesupport/test/core_ext/class/delegating_attributes_test.rb b/activesupport/test/core_ext/class/delegating_attributes_test.rb
deleted file mode 100644
index 447b1d10ad..0000000000
--- a/activesupport/test/core_ext/class/delegating_attributes_test.rb
+++ /dev/null
@@ -1,122 +0,0 @@
-require 'abstract_unit'
-require 'active_support/core_ext/class/delegating_attributes'
-
-module DelegatingFixtures
- class Parent
- end
-
- class Child < Parent
- ActiveSupport::Deprecation.silence do
- superclass_delegating_accessor :some_attribute
- end
- end
-
- class Mokopuna < Child
- end
-
- class PercysMom
- ActiveSupport::Deprecation.silence do
- superclass_delegating_accessor :superpower
- end
- end
-
- class Percy < PercysMom
- end
-end
-
-class DelegatingAttributesTest < ActiveSupport::TestCase
- include DelegatingFixtures
- attr_reader :single_class
-
- def setup
- @single_class = Class.new(Object)
- end
-
- def test_simple_accessor_declaration
- assert_deprecated do
- single_class.superclass_delegating_accessor :both
- end
-
- # Class should have accessor and mutator
- # the instance should have an accessor only
- assert_respond_to single_class, :both
- assert_respond_to single_class, :both=
- assert single_class.public_instance_methods.map(&:to_s).include?("both")
- assert !single_class.public_instance_methods.map(&:to_s).include?("both=")
- end
-
- def test_simple_accessor_declaration_with_instance_reader_false
- _instance_methods = single_class.public_instance_methods
-
- assert_deprecated do
- single_class.superclass_delegating_accessor :no_instance_reader, :instance_reader => false
- end
-
- assert_respond_to single_class, :no_instance_reader
- assert_respond_to single_class, :no_instance_reader=
- assert !_instance_methods.include?(:no_instance_reader)
- assert !_instance_methods.include?(:no_instance_reader?)
- assert !_instance_methods.include?(:_no_instance_reader)
- end
-
- def test_working_with_simple_attributes
- assert_deprecated do
- single_class.superclass_delegating_accessor :both
- end
-
- single_class.both = "HMMM"
-
- assert_equal "HMMM", single_class.both
- assert_equal true, single_class.both?
-
- assert_equal "HMMM", single_class.new.both
- assert_equal true, single_class.new.both?
-
- single_class.both = false
- assert_equal false, single_class.both?
- end
-
- def test_child_class_delegates_to_parent_but_can_be_overridden
- parent = Class.new
-
- assert_deprecated do
- parent.superclass_delegating_accessor :both
- end
-
- child = Class.new(parent)
- parent.both = "1"
- assert_equal "1", child.both
-
- child.both = "2"
- assert_equal "1", parent.both
- assert_equal "2", child.both
-
- parent.both = "3"
- assert_equal "3", parent.both
- assert_equal "2", child.both
- end
-
- def test_delegation_stops_at_the_right_level
- assert_nil Percy.superpower
- assert_nil PercysMom.superpower
-
- PercysMom.superpower = :heatvision
- assert_equal :heatvision, Percy.superpower
- end
-
- def test_delegation_stops_for_nil
- Mokopuna.some_attribute = nil
- Child.some_attribute="1"
-
- assert_equal "1", Child.some_attribute
- assert_nil Mokopuna.some_attribute
- ensure
- Child.some_attribute=nil
- end
-
- def test_deprecation_warning
- assert_deprecated(/superclass_delegating_accessor is deprecated/) do
- single_class.superclass_delegating_accessor :test_attribute
- end
- end
-end
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index e89be25b53..0fc3f765f5 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -191,8 +191,9 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
def test_yesterday_constructor_when_zone_is_set
with_env_tz 'UTC' do
with_tz_default ActiveSupport::TimeZone['Eastern Time (US & Canada)'] do # UTC -5
- Time.stubs(:now).returns Time.local(2000, 1, 1)
- assert_equal Date.new(1999, 12, 30), Date.yesterday
+ Time.stub(:now, Time.local(2000, 1, 1)) do
+ assert_equal Date.new(1999, 12, 30), Date.yesterday
+ end
end
end
end
@@ -212,8 +213,9 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
def test_tomorrow_constructor_when_zone_is_set
with_env_tz 'UTC' do
with_tz_default ActiveSupport::TimeZone['Europe/Paris'] do # UTC +1
- Time.stubs(:now).returns Time.local(1999, 12, 31, 23)
- assert_equal Date.new(2000, 1, 2), Date.tomorrow
+ Time.stub(:now, Time.local(1999, 12, 31, 23)) do
+ assert_equal Date.new(2000, 1, 2), Date.tomorrow
+ end
end
end
end
@@ -317,23 +319,26 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
end
def test_past
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal true, Date.new(1999, 12, 31).past?
- assert_equal false, Date.new(2000,1,1).past?
- assert_equal false, Date.new(2000,1,2).past?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal true, Date.new(1999, 12, 31).past?
+ assert_equal false, Date.new(2000,1,1).past?
+ assert_equal false, Date.new(2000,1,2).past?
+ end
end
def test_future
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal false, Date.new(1999, 12, 31).future?
- assert_equal false, Date.new(2000,1,1).future?
- assert_equal true, Date.new(2000,1,2).future?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal false, Date.new(1999, 12, 31).future?
+ assert_equal false, Date.new(2000,1,1).future?
+ assert_equal true, Date.new(2000,1,2).future?
+ end
end
def test_current_returns_date_today_when_zone_not_set
with_env_tz 'US/Central' do
- Time.stubs(:now).returns Time.local(1999, 12, 31, 23)
- assert_equal Date.today, Date.current
+ Time.stub(:now, Time.local(1999, 12, 31, 23)) do
+ assert_equal Date.today, Date.current
+ end
end
end
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index 74319ecd09..58d79f0541 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -204,61 +204,69 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_today_with_offset
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal false, DateTime.civil(1999,12,31,23,59,59, Rational(-18000, 86400)).today?
- assert_equal true, DateTime.civil(2000,1,1,0,0,0, Rational(-18000, 86400)).today?
- assert_equal true, DateTime.civil(2000,1,1,23,59,59, Rational(-18000, 86400)).today?
- assert_equal false, DateTime.civil(2000,1,2,0,0,0, Rational(-18000, 86400)).today?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal false, DateTime.civil(1999,12,31,23,59,59, Rational(-18000, 86400)).today?
+ assert_equal true, DateTime.civil(2000,1,1,0,0,0, Rational(-18000, 86400)).today?
+ assert_equal true, DateTime.civil(2000,1,1,23,59,59, Rational(-18000, 86400)).today?
+ assert_equal false, DateTime.civil(2000,1,2,0,0,0, Rational(-18000, 86400)).today?
+ end
end
def test_today_without_offset
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal false, DateTime.civil(1999,12,31,23,59,59).today?
- assert_equal true, DateTime.civil(2000,1,1,0).today?
- assert_equal true, DateTime.civil(2000,1,1,23,59,59).today?
- assert_equal false, DateTime.civil(2000,1,2,0).today?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal false, DateTime.civil(1999,12,31,23,59,59).today?
+ assert_equal true, DateTime.civil(2000,1,1,0).today?
+ assert_equal true, DateTime.civil(2000,1,1,23,59,59).today?
+ assert_equal false, DateTime.civil(2000,1,2,0).today?
+ end
end
def test_past_with_offset
- DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)))
- assert_equal true, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).past?
- assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).past?
- assert_equal false, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).past?
+ DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do
+ assert_equal true, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).past?
+ assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).past?
+ assert_equal false, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).past?
+ end
end
def test_past_without_offset
- DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)))
- assert_equal true, DateTime.civil(2005,2,10,20,30,44).past?
- assert_equal false, DateTime.civil(2005,2,10,20,30,45).past?
- assert_equal false, DateTime.civil(2005,2,10,20,30,46).past?
+ DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do
+ assert_equal true, DateTime.civil(2005,2,10,20,30,44).past?
+ assert_equal false, DateTime.civil(2005,2,10,20,30,45).past?
+ assert_equal false, DateTime.civil(2005,2,10,20,30,46).past?
+ end
end
def test_future_with_offset
- DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)))
- assert_equal false, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).future?
- assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).future?
- assert_equal true, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).future?
+ DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do
+ assert_equal false, DateTime.civil(2005,2,10,15,30,44, Rational(-18000, 86400)).future?
+ assert_equal false, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)).future?
+ assert_equal true, DateTime.civil(2005,2,10,15,30,46, Rational(-18000, 86400)).future?
+ end
end
def test_future_without_offset
- DateTime.stubs(:current).returns(DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400)))
- assert_equal false, DateTime.civil(2005,2,10,20,30,44).future?
- assert_equal false, DateTime.civil(2005,2,10,20,30,45).future?
- assert_equal true, DateTime.civil(2005,2,10,20,30,46).future?
+ DateTime.stub(:current, DateTime.civil(2005,2,10,15,30,45, Rational(-18000, 86400))) do
+ assert_equal false, DateTime.civil(2005,2,10,20,30,44).future?
+ assert_equal false, DateTime.civil(2005,2,10,20,30,45).future?
+ assert_equal true, DateTime.civil(2005,2,10,20,30,46).future?
+ end
end
def test_current_returns_date_today_when_zone_is_not_set
with_env_tz 'US/Eastern' do
- Time.stubs(:now).returns Time.local(1999, 12, 31, 23, 59, 59)
- assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current
+ Time.stub(:now, Time.local(1999, 12, 31, 23, 59, 59)) do
+ assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current
+ end
end
end
def test_current_returns_time_zone_today_when_zone_is_set
Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
with_env_tz 'US/Eastern' do
- Time.stubs(:now).returns Time.local(1999, 12, 31, 23, 59, 59)
- assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current
+ Time.stub(:now, Time.local(1999, 12, 31, 23, 59, 59)) do
+ assert_equal DateTime.new(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)), DateTime.current
+ end
end
ensure
Time.zone = nil
diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb
index c283b546e6..9e97acaffb 100644
--- a/activesupport/test/core_ext/duration_test.rb
+++ b/activesupport/test/core_ext/duration_test.rb
@@ -140,28 +140,30 @@ class DurationTest < ActiveSupport::TestCase
def test_since_and_ago_anchored_to_time_now_when_time_zone_is_not_set
Time.zone = nil
with_env_tz 'US/Eastern' do
- Time.stubs(:now).returns Time.local(2000)
- # since
- assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.since
- assert_equal Time.local(2000,1,1,0,0,5), 5.seconds.since
- # ago
- assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago
- assert_equal Time.local(1999,12,31,23,59,55), 5.seconds.ago
+ Time.stub(:now, Time.local(2000)) do
+ # since
+ assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.since
+ assert_equal Time.local(2000,1,1,0,0,5), 5.seconds.since
+ # ago
+ assert_not_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago
+ assert_equal Time.local(1999,12,31,23,59,55), 5.seconds.ago
+ end
end
end
def test_since_and_ago_anchored_to_time_zone_now_when_time_zone_is_set
Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
with_env_tz 'US/Eastern' do
- Time.stubs(:now).returns Time.local(2000)
- # since
- assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.since
- assert_equal Time.utc(2000,1,1,0,0,5), 5.seconds.since.time
- assert_equal 'Eastern Time (US & Canada)', 5.seconds.since.time_zone.name
- # ago
- assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago
- assert_equal Time.utc(1999,12,31,23,59,55), 5.seconds.ago.time
- assert_equal 'Eastern Time (US & Canada)', 5.seconds.ago.time_zone.name
+ Time.stub(:now, Time.local(2000)) do
+ # since
+ assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.since
+ assert_equal Time.utc(2000,1,1,0,0,5), 5.seconds.since.time
+ assert_equal 'Eastern Time (US & Canada)', 5.seconds.since.time_zone.name
+ # ago
+ assert_instance_of ActiveSupport::TimeWithZone, 5.seconds.ago
+ assert_equal Time.utc(1999,12,31,23,59,55), 5.seconds.ago.time
+ assert_equal 'Eastern Time (US & Canada)', 5.seconds.ago.time_zone.name
+ end
end
ensure
Time.zone = nil
diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb
index e5d8ae7882..f09b7d8850 100644
--- a/activesupport/test/core_ext/enumerable_test.rb
+++ b/activesupport/test/core_ext/enumerable_test.rb
@@ -3,6 +3,8 @@ require 'active_support/core_ext/array'
require 'active_support/core_ext/enumerable'
Payment = Struct.new(:price)
+ExpandedPayment = Struct.new(:dollars, :cents)
+
class SummablePayment < Payment
def +(p) self.class.new(price + p.price) end
end
@@ -110,4 +112,16 @@ class EnumerableTests < ActiveSupport::TestCase
assert_equal [1, 2, 4], (1..5).to_set.without(3, 5)
assert_equal({foo: 1, baz: 3}, {foo: 1, bar: 2, baz: 3}.without(:bar))
end
+
+ def test_pluck
+ payments = GenericEnumerable.new([ Payment.new(5), Payment.new(15), Payment.new(10) ])
+ assert_equal [5, 15, 10], payments.pluck(:price)
+
+ payments = GenericEnumerable.new([
+ ExpandedPayment.new(5, 99),
+ ExpandedPayment.new(15, 0),
+ ExpandedPayment.new(10, 50)
+ ])
+ assert_equal [[5, 99], [15, 0], [10, 50]], payments.pluck(:dollars, :cents)
+ end
end
diff --git a/activesupport/test/core_ext/marshal_test.rb b/activesupport/test/core_ext/marshal_test.rb
index e49330128b..825df439a5 100644
--- a/activesupport/test/core_ext/marshal_test.rb
+++ b/activesupport/test/core_ext/marshal_test.rb
@@ -8,7 +8,7 @@ class MarshalTest < ActiveSupport::TestCase
def teardown
ActiveSupport::Dependencies.clear
- remove_constants(:E, :ClassFolder)
+ remove_constants(:EM, :ClassFolder)
end
test "that Marshal#load still works" do
@@ -22,14 +22,14 @@ class MarshalTest < ActiveSupport::TestCase
test "that a missing class is autoloaded from string" do
dumped = nil
with_autoloading_fixtures do
- dumped = Marshal.dump(E.new)
+ dumped = Marshal.dump(EM.new)
end
- remove_constants(:E)
+ remove_constants(:EM)
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
- assert_kind_of E, Marshal.load(dumped)
+ assert_kind_of EM, Marshal.load(dumped)
end
end
@@ -50,16 +50,16 @@ class MarshalTest < ActiveSupport::TestCase
test "that more than one missing class is autoloaded" do
dumped = nil
with_autoloading_fixtures do
- dumped = Marshal.dump([E.new, ClassFolder.new])
+ dumped = Marshal.dump([EM.new, ClassFolder.new])
end
- remove_constants(:E, :ClassFolder)
+ remove_constants(:EM, :ClassFolder)
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
loaded = Marshal.load(dumped)
assert_equal 2, loaded.size
- assert_kind_of E, loaded[0]
+ assert_kind_of EM, loaded[0]
assert_kind_of ClassFolder, loaded[1]
end
end
@@ -67,10 +67,10 @@ class MarshalTest < ActiveSupport::TestCase
test "that a real missing class is causing an exception" do
dumped = nil
with_autoloading_fixtures do
- dumped = Marshal.dump(E.new)
+ dumped = Marshal.dump(EM.new)
end
- remove_constants(:E)
+ remove_constants(:EM)
ActiveSupport::Dependencies.clear
assert_raise(NameError) do
@@ -84,10 +84,10 @@ class MarshalTest < ActiveSupport::TestCase
end
with_autoloading_fixtures do
- dumped = Marshal.dump([E.new, SomeClass.new])
+ dumped = Marshal.dump([EM.new, SomeClass.new])
end
- remove_constants(:E)
+ remove_constants(:EM)
self.class.send(:remove_const, :SomeClass)
ActiveSupport::Dependencies.clear
@@ -96,8 +96,8 @@ class MarshalTest < ActiveSupport::TestCase
Marshal.load(dumped)
end
- assert_nothing_raised("E failed to load while we expect only SomeClass to fail loading") do
- E.new
+ assert_nothing_raised("EM failed to load while we expect only SomeClass to fail loading") do
+ EM.new
end
assert_raise(NameError, "We expected SomeClass to not be loaded but it is!") do
@@ -109,15 +109,15 @@ class MarshalTest < ActiveSupport::TestCase
test "loading classes from files trigger autoloading" do
Tempfile.open("object_serializer_test") do |f|
with_autoloading_fixtures do
- Marshal.dump(E.new, f)
+ Marshal.dump(EM.new, f)
end
f.rewind
- remove_constants(:E)
+ remove_constants(:EM)
ActiveSupport::Dependencies.clear
with_autoloading_fixtures do
- assert_kind_of E, Marshal.load(f)
+ assert_kind_of EM, Marshal.load(f)
end
end
end
diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb
index b82448458d..2d8796179e 100644
--- a/activesupport/test/core_ext/numeric_ext_test.rb
+++ b/activesupport/test/core_ext/numeric_ext_test.rb
@@ -389,4 +389,88 @@ class NumericExtFormattingTest < ActiveSupport::TestCase
def test_in_milliseconds
assert_equal 10_000, 10.seconds.in_milliseconds
end
+
+ # TODO: Remove positive and negative tests when we drop support to ruby < 2.3
+ b = 2**64
+ b *= b until Bignum === b
+
+ T_ZERO = b.coerce(0).first
+ T_ONE = b.coerce(1).first
+ T_MONE = b.coerce(-1).first
+
+ def test_positive
+ assert_predicate(1, :positive?)
+ assert_not_predicate(0, :positive?)
+ assert_not_predicate(-1, :positive?)
+ assert_predicate(+1.0, :positive?)
+ assert_not_predicate(+0.0, :positive?)
+ assert_not_predicate(-0.0, :positive?)
+ assert_not_predicate(-1.0, :positive?)
+ assert_predicate(+(0.0.next_float), :positive?)
+ assert_not_predicate(-(0.0.next_float), :positive?)
+ assert_predicate(Float::INFINITY, :positive?)
+ assert_not_predicate(-Float::INFINITY, :positive?)
+ assert_not_predicate(Float::NAN, :positive?)
+
+ a = Class.new(Numeric) do
+ def >(x); true; end
+ end.new
+ assert_predicate(a, :positive?)
+
+ a = Class.new(Numeric) do
+ def >(x); false; end
+ end.new
+ assert_not_predicate(a, :positive?)
+
+ assert_predicate(1/2r, :positive?)
+ assert_not_predicate(-1/2r, :positive?)
+
+ assert_predicate(T_ONE, :positive?)
+ assert_not_predicate(T_MONE, :positive?)
+ assert_not_predicate(T_ZERO, :positive?)
+
+ e = assert_raises(NoMethodError) do
+ Complex(1).positive?
+ end
+
+ assert_match(/positive\?/, e.message)
+ end
+
+ def test_negative
+ assert_predicate(-1, :negative?)
+ assert_not_predicate(0, :negative?)
+ assert_not_predicate(1, :negative?)
+ assert_predicate(-1.0, :negative?)
+ assert_not_predicate(-0.0, :negative?)
+ assert_not_predicate(+0.0, :negative?)
+ assert_not_predicate(+1.0, :negative?)
+ assert_predicate(-(0.0.next_float), :negative?)
+ assert_not_predicate(+(0.0.next_float), :negative?)
+ assert_predicate(-Float::INFINITY, :negative?)
+ assert_not_predicate(Float::INFINITY, :negative?)
+ assert_not_predicate(Float::NAN, :negative?)
+
+ a = Class.new(Numeric) do
+ def <(x); true; end
+ end.new
+ assert_predicate(a, :negative?)
+
+ a = Class.new(Numeric) do
+ def <(x); false; end
+ end.new
+ assert_not_predicate(a, :negative?)
+
+ assert_predicate(-1/2r, :negative?)
+ assert_not_predicate(1/2r, :negative?)
+
+ assert_not_predicate(T_ONE, :negative?)
+ assert_predicate(T_MONE, :negative?)
+ assert_not_predicate(T_ZERO, :negative?)
+
+ e = assert_raises(NoMethodError) do
+ Complex(1).negative?
+ end
+
+ assert_match(/negative\?/, e.message)
+ end
end
diff --git a/activesupport/test/core_ext/object/try_test.rb b/activesupport/test/core_ext/object/try_test.rb
index 89438675c1..5ea0f0eca6 100644
--- a/activesupport/test/core_ext/object/try_test.rb
+++ b/activesupport/test/core_ext/object/try_test.rb
@@ -77,9 +77,9 @@ class ObjectTryTest < ActiveSupport::TestCase
klass = Class.new do
private
- def private_method
- 'private method'
- end
+ def private_method
+ 'private method'
+ end
end
assert_raise(NoMethodError) { klass.new.try!(:private_method) }
@@ -89,11 +89,75 @@ class ObjectTryTest < ActiveSupport::TestCase
klass = Class.new do
private
- def private_method
- 'private method'
- end
+ def private_method
+ 'private method'
+ end
end
assert_nil klass.new.try(:private_method)
end
+
+ class Decorator < SimpleDelegator
+ def delegator_method
+ 'delegator method'
+ end
+
+ def reverse
+ 'overridden reverse'
+ end
+
+ private
+
+ def private_delegator_method
+ 'private delegator method'
+ end
+ end
+
+ def test_try_with_method_on_delegator
+ assert_equal 'delegator method', Decorator.new(@string).try(:delegator_method)
+ end
+
+ def test_try_with_method_on_delegator_target
+ assert_equal 5, Decorator.new(@string).size
+ end
+
+ def test_try_with_overriden_method_on_delegator
+ assert_equal 'overridden reverse', Decorator.new(@string).reverse
+ end
+
+ def test_try_with_private_method_on_delegator
+ assert_nil Decorator.new(@string).try(:private_delegator_method)
+ end
+
+ def test_try_with_private_method_on_delegator_bang
+ assert_raise(NoMethodError) do
+ Decorator.new(@string).try!(:private_delegator_method)
+ end
+ end
+
+ def test_try_with_private_method_on_delegator_target
+ klass = Class.new do
+ private
+
+ def private_method
+ 'private method'
+ end
+ end
+
+ assert_nil Decorator.new(klass.new).try(:private_method)
+ end
+
+ def test_try_with_private_method_on_delegator_target_bang
+ klass = Class.new do
+ private
+
+ def private_method
+ 'private method'
+ end
+ end
+
+ assert_raise(NoMethodError) do
+ Decorator.new(klass.new).try!(:private_method)
+ end
+ end
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index cb24147ab3..3a5d6df06d 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -435,117 +435,123 @@ class StringConversionsTest < ActiveSupport::TestCase
def test_standard_time_string_to_time_when_current_time_is_standard_time
with_env_tz "US/Eastern" do
- Time.stubs(:now).returns(Time.local(2012, 1, 1))
- assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time
- assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 -0800".to_time
- assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 -0800".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 -0500".to_time
- assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 -0500".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 5, 0), "2012-01-01 10:00 UTC".to_time
- assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00 UTC".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 PST".to_time
- assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 PST".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 EST".to_time
- assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 EST".to_time(:utc)
+ Time.stub(:now, Time.local(2012, 1, 1)) do
+ assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time
+ assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 -0800".to_time
+ assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 -0800".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 -0500".to_time
+ assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 -0500".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 5, 0), "2012-01-01 10:00 UTC".to_time
+ assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00 UTC".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 PST".to_time
+ assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 PST".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 EST".to_time
+ assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 EST".to_time(:utc)
+ end
end
end
def test_standard_time_string_to_time_when_current_time_is_daylight_savings
with_env_tz "US/Eastern" do
- Time.stubs(:now).returns(Time.local(2012, 7, 1))
- assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time
- assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 -0800".to_time
- assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 -0800".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 -0500".to_time
- assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 -0500".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 5, 0), "2012-01-01 10:00 UTC".to_time
- assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00 UTC".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 PST".to_time
- assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 PST".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 EST".to_time
- assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 EST".to_time(:utc)
+ Time.stub(:now, Time.local(2012, 7, 1)) do
+ assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time
+ assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 -0800".to_time
+ assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 -0800".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 -0500".to_time
+ assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 -0500".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 5, 0), "2012-01-01 10:00 UTC".to_time
+ assert_equal Time.utc(2012, 1, 1, 10, 0), "2012-01-01 10:00 UTC".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 13, 0), "2012-01-01 10:00 PST".to_time
+ assert_equal Time.utc(2012, 1, 1, 18, 0), "2012-01-01 10:00 PST".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 10, 0), "2012-01-01 10:00 EST".to_time
+ assert_equal Time.utc(2012, 1, 1, 15, 0), "2012-01-01 10:00 EST".to_time(:utc)
+ end
end
end
def test_daylight_savings_string_to_time_when_current_time_is_standard_time
with_env_tz "US/Eastern" do
- Time.stubs(:now).returns(Time.local(2012, 1, 1))
- assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time
- assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 -0700".to_time
- assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 -0700".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 -0400".to_time
- assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 -0400".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 6, 0), "2012-07-01 10:00 UTC".to_time
- assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00 UTC".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 PDT".to_time
- assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 PDT".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 EDT".to_time
- assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 EDT".to_time(:utc)
+ Time.stub(:now, Time.local(2012, 1, 1)) do
+ assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time
+ assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 -0700".to_time
+ assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 -0700".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 -0400".to_time
+ assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 -0400".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 6, 0), "2012-07-01 10:00 UTC".to_time
+ assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00 UTC".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 PDT".to_time
+ assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 PDT".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 EDT".to_time
+ assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 EDT".to_time(:utc)
+ end
end
end
def test_daylight_savings_string_to_time_when_current_time_is_daylight_savings
with_env_tz "US/Eastern" do
- Time.stubs(:now).returns(Time.local(2012, 7, 1))
- assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time
- assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 -0700".to_time
- assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 -0700".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 -0400".to_time
- assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 -0400".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 6, 0), "2012-07-01 10:00 UTC".to_time
- assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00 UTC".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 PDT".to_time
- assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 PDT".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 EDT".to_time
- assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 EDT".to_time(:utc)
+ Time.stub(:now, Time.local(2012, 7, 1)) do
+ assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time
+ assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 -0700".to_time
+ assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 -0700".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 -0400".to_time
+ assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 -0400".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 6, 0), "2012-07-01 10:00 UTC".to_time
+ assert_equal Time.utc(2012, 7, 1, 10, 0), "2012-07-01 10:00 UTC".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 13, 0), "2012-07-01 10:00 PDT".to_time
+ assert_equal Time.utc(2012, 7, 1, 17, 0), "2012-07-01 10:00 PDT".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 10, 0), "2012-07-01 10:00 EDT".to_time
+ assert_equal Time.utc(2012, 7, 1, 14, 0), "2012-07-01 10:00 EDT".to_time(:utc)
+ end
end
end
def test_partial_string_to_time_when_current_time_is_standard_time
with_env_tz "US/Eastern" do
- Time.stubs(:now).returns(Time.local(2012, 1, 1))
- assert_equal Time.local(2012, 1, 1, 10, 0), "10:00".to_time
- assert_equal Time.utc(2012, 1, 1, 10, 0), "10:00".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 6, 0), "10:00 -0100".to_time
- assert_equal Time.utc(2012, 1, 1, 11, 0), "10:00 -0100".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 10, 0), "10:00 -0500".to_time
- assert_equal Time.utc(2012, 1, 1, 15, 0), "10:00 -0500".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 5, 0), "10:00 UTC".to_time
- assert_equal Time.utc(2012, 1, 1, 10, 0), "10:00 UTC".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 13, 0), "10:00 PST".to_time
- assert_equal Time.utc(2012, 1, 1, 18, 0), "10:00 PST".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 12, 0), "10:00 PDT".to_time
- assert_equal Time.utc(2012, 1, 1, 17, 0), "10:00 PDT".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 10, 0), "10:00 EST".to_time
- assert_equal Time.utc(2012, 1, 1, 15, 0), "10:00 EST".to_time(:utc)
- assert_equal Time.local(2012, 1, 1, 9, 0), "10:00 EDT".to_time
- assert_equal Time.utc(2012, 1, 1, 14, 0), "10:00 EDT".to_time(:utc)
+ Time.stub(:now, Time.local(2012, 1, 1)) do
+ assert_equal Time.local(2012, 1, 1, 10, 0), "10:00".to_time
+ assert_equal Time.utc(2012, 1, 1, 10, 0), "10:00".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 6, 0), "10:00 -0100".to_time
+ assert_equal Time.utc(2012, 1, 1, 11, 0), "10:00 -0100".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 10, 0), "10:00 -0500".to_time
+ assert_equal Time.utc(2012, 1, 1, 15, 0), "10:00 -0500".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 5, 0), "10:00 UTC".to_time
+ assert_equal Time.utc(2012, 1, 1, 10, 0), "10:00 UTC".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 13, 0), "10:00 PST".to_time
+ assert_equal Time.utc(2012, 1, 1, 18, 0), "10:00 PST".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 12, 0), "10:00 PDT".to_time
+ assert_equal Time.utc(2012, 1, 1, 17, 0), "10:00 PDT".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 10, 0), "10:00 EST".to_time
+ assert_equal Time.utc(2012, 1, 1, 15, 0), "10:00 EST".to_time(:utc)
+ assert_equal Time.local(2012, 1, 1, 9, 0), "10:00 EDT".to_time
+ assert_equal Time.utc(2012, 1, 1, 14, 0), "10:00 EDT".to_time(:utc)
+ end
end
end
def test_partial_string_to_time_when_current_time_is_daylight_savings
with_env_tz "US/Eastern" do
- Time.stubs(:now).returns(Time.local(2012, 7, 1))
- assert_equal Time.local(2012, 7, 1, 10, 0), "10:00".to_time
- assert_equal Time.utc(2012, 7, 1, 10, 0), "10:00".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 7, 0), "10:00 -0100".to_time
- assert_equal Time.utc(2012, 7, 1, 11, 0), "10:00 -0100".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 11, 0), "10:00 -0500".to_time
- assert_equal Time.utc(2012, 7, 1, 15, 0), "10:00 -0500".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 6, 0), "10:00 UTC".to_time
- assert_equal Time.utc(2012, 7, 1, 10, 0), "10:00 UTC".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 14, 0), "10:00 PST".to_time
- assert_equal Time.utc(2012, 7, 1, 18, 0), "10:00 PST".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 13, 0), "10:00 PDT".to_time
- assert_equal Time.utc(2012, 7, 1, 17, 0), "10:00 PDT".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 11, 0), "10:00 EST".to_time
- assert_equal Time.utc(2012, 7, 1, 15, 0), "10:00 EST".to_time(:utc)
- assert_equal Time.local(2012, 7, 1, 10, 0), "10:00 EDT".to_time
- assert_equal Time.utc(2012, 7, 1, 14, 0), "10:00 EDT".to_time(:utc)
+ Time.stub(:now, Time.local(2012, 7, 1)) do
+ assert_equal Time.local(2012, 7, 1, 10, 0), "10:00".to_time
+ assert_equal Time.utc(2012, 7, 1, 10, 0), "10:00".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 7, 0), "10:00 -0100".to_time
+ assert_equal Time.utc(2012, 7, 1, 11, 0), "10:00 -0100".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 11, 0), "10:00 -0500".to_time
+ assert_equal Time.utc(2012, 7, 1, 15, 0), "10:00 -0500".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 6, 0), "10:00 UTC".to_time
+ assert_equal Time.utc(2012, 7, 1, 10, 0), "10:00 UTC".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 14, 0), "10:00 PST".to_time
+ assert_equal Time.utc(2012, 7, 1, 18, 0), "10:00 PST".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 13, 0), "10:00 PDT".to_time
+ assert_equal Time.utc(2012, 7, 1, 17, 0), "10:00 PDT".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 11, 0), "10:00 EST".to_time
+ assert_equal Time.utc(2012, 7, 1, 15, 0), "10:00 EST".to_time(:utc)
+ assert_equal Time.local(2012, 7, 1, 10, 0), "10:00 EDT".to_time
+ assert_equal Time.utc(2012, 7, 1, 14, 0), "10:00 EDT".to_time(:utc)
+ end
end
end
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index d59775001b..e1ac5b3d8d 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -149,6 +149,9 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(2006,3,19,23,59,59,Rational(999999999, 1000)), Time.local(2006,3,19,10,10,10).end_of_day, 'ends DST'
assert_equal Time.local(2006,10,1,23,59,59,Rational(999999999, 1000)), Time.local(2006,10,1,10,10,10).end_of_day, 'start DST'
end
+ with_env_tz 'Asia/Yekaterinburg' do
+ assert_equal Time.local(2015, 2, 8, 23, 59, 59, Rational(999999999, 1000)), Time.new(2015, 2, 8, 8, 0, 0, '+05:00').end_of_day
+ end
end
def test_end_of_hour
@@ -389,6 +392,7 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
assert_equal Time.local(2005,1,2,11,22,33, 8), Time.local(2005,1,2,11,22,33,44).change(:usec => 8)
assert_equal Time.local(2005,1,2,11,22,33, 8), Time.local(2005,1,2,11,22,33,2).change(:nsec => 8000)
assert_raise(ArgumentError) { Time.local(2005,1,2,11,22,33, 8).change(:usec => 1, :nsec => 1) }
+ assert_nothing_raised(ArgumentError) { Time.new(2015, 5, 9, 10, 00, 00, '+03:00').change(nsec: 999999999) }
end
def test_utc_change
@@ -601,13 +605,15 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_days_in_month_feb_in_common_year_without_year_arg
- Time.stubs(:now).returns(Time.utc(2007))
- assert_equal 28, Time.days_in_month(2)
+ Time.stub(:now, Time.utc(2007)) do
+ assert_equal 28, Time.days_in_month(2)
+ end
end
def test_days_in_month_feb_in_leap_year_without_year_arg
- Time.stubs(:now).returns(Time.utc(2008))
- assert_equal 29, Time.days_in_month(2)
+ Time.stub(:now, Time.utc(2008)) do
+ assert_equal 29, Time.days_in_month(2)
+ end
end
def test_last_month_on_31st
@@ -619,68 +625,74 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
def test_today_with_time_local
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal false, Time.local(1999,12,31,23,59,59).today?
- assert_equal true, Time.local(2000,1,1,0).today?
- assert_equal true, Time.local(2000,1,1,23,59,59).today?
- assert_equal false, Time.local(2000,1,2,0).today?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal false, Time.local(1999,12,31,23,59,59).today?
+ assert_equal true, Time.local(2000,1,1,0).today?
+ assert_equal true, Time.local(2000,1,1,23,59,59).today?
+ assert_equal false, Time.local(2000,1,2,0).today?
+ end
end
def test_today_with_time_utc
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal false, Time.utc(1999,12,31,23,59,59).today?
- assert_equal true, Time.utc(2000,1,1,0).today?
- assert_equal true, Time.utc(2000,1,1,23,59,59).today?
- assert_equal false, Time.utc(2000,1,2,0).today?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal false, Time.utc(1999,12,31,23,59,59).today?
+ assert_equal true, Time.utc(2000,1,1,0).today?
+ assert_equal true, Time.utc(2000,1,1,23,59,59).today?
+ assert_equal false, Time.utc(2000,1,2,0).today?
+ end
end
def test_past_with_time_current_as_time_local
with_env_tz 'US/Eastern' do
- Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45))
- assert_equal true, Time.local(2005,2,10,15,30,44).past?
- assert_equal false, Time.local(2005,2,10,15,30,45).past?
- assert_equal false, Time.local(2005,2,10,15,30,46).past?
- assert_equal true, Time.utc(2005,2,10,20,30,44).past?
- assert_equal false, Time.utc(2005,2,10,20,30,45).past?
- assert_equal false, Time.utc(2005,2,10,20,30,46).past?
+ Time.stub(:current, Time.local(2005,2,10,15,30,45)) do
+ assert_equal true, Time.local(2005,2,10,15,30,44).past?
+ assert_equal false, Time.local(2005,2,10,15,30,45).past?
+ assert_equal false, Time.local(2005,2,10,15,30,46).past?
+ assert_equal true, Time.utc(2005,2,10,20,30,44).past?
+ assert_equal false, Time.utc(2005,2,10,20,30,45).past?
+ assert_equal false, Time.utc(2005,2,10,20,30,46).past?
+ end
end
end
def test_past_with_time_current_as_time_with_zone
with_env_tz 'US/Eastern' do
twz = Time.utc(2005,2,10,15,30,45).in_time_zone('Central Time (US & Canada)')
- Time.stubs(:current).returns(twz)
- assert_equal true, Time.local(2005,2,10,10,30,44).past?
- assert_equal false, Time.local(2005,2,10,10,30,45).past?
- assert_equal false, Time.local(2005,2,10,10,30,46).past?
- assert_equal true, Time.utc(2005,2,10,15,30,44).past?
- assert_equal false, Time.utc(2005,2,10,15,30,45).past?
- assert_equal false, Time.utc(2005,2,10,15,30,46).past?
+ Time.stub(:current, twz) do
+ assert_equal true, Time.local(2005,2,10,10,30,44).past?
+ assert_equal false, Time.local(2005,2,10,10,30,45).past?
+ assert_equal false, Time.local(2005,2,10,10,30,46).past?
+ assert_equal true, Time.utc(2005,2,10,15,30,44).past?
+ assert_equal false, Time.utc(2005,2,10,15,30,45).past?
+ assert_equal false, Time.utc(2005,2,10,15,30,46).past?
+ end
end
end
def test_future_with_time_current_as_time_local
with_env_tz 'US/Eastern' do
- Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45))
- assert_equal false, Time.local(2005,2,10,15,30,44).future?
- assert_equal false, Time.local(2005,2,10,15,30,45).future?
- assert_equal true, Time.local(2005,2,10,15,30,46).future?
- assert_equal false, Time.utc(2005,2,10,20,30,44).future?
- assert_equal false, Time.utc(2005,2,10,20,30,45).future?
- assert_equal true, Time.utc(2005,2,10,20,30,46).future?
+ Time.stub(:current, Time.local(2005,2,10,15,30,45)) do
+ assert_equal false, Time.local(2005,2,10,15,30,44).future?
+ assert_equal false, Time.local(2005,2,10,15,30,45).future?
+ assert_equal true, Time.local(2005,2,10,15,30,46).future?
+ assert_equal false, Time.utc(2005,2,10,20,30,44).future?
+ assert_equal false, Time.utc(2005,2,10,20,30,45).future?
+ assert_equal true, Time.utc(2005,2,10,20,30,46).future?
+ end
end
end
def test_future_with_time_current_as_time_with_zone
with_env_tz 'US/Eastern' do
twz = Time.utc(2005,2,10,15,30,45).in_time_zone('Central Time (US & Canada)')
- Time.stubs(:current).returns(twz)
- assert_equal false, Time.local(2005,2,10,10,30,44).future?
- assert_equal false, Time.local(2005,2,10,10,30,45).future?
- assert_equal true, Time.local(2005,2,10,10,30,46).future?
- assert_equal false, Time.utc(2005,2,10,15,30,44).future?
- assert_equal false, Time.utc(2005,2,10,15,30,45).future?
- assert_equal true, Time.utc(2005,2,10,15,30,46).future?
+ Time.stub(:current, twz) do
+ assert_equal false, Time.local(2005,2,10,10,30,44).future?
+ assert_equal false, Time.local(2005,2,10,10,30,45).future?
+ assert_equal true, Time.local(2005,2,10,10,30,46).future?
+ assert_equal false, Time.utc(2005,2,10,15,30,44).future?
+ assert_equal false, Time.utc(2005,2,10,15,30,45).future?
+ assert_equal true, Time.utc(2005,2,10,15,30,46).future?
+ end
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 79d78c02cd..5b48bf328c 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -205,45 +205,50 @@ class TimeWithZoneTest < ActiveSupport::TestCase
end
def test_today
- Date.stubs(:current).returns(Date.new(2000, 1, 1))
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(1999,12,31,23,59,59) ).today?
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,0) ).today?
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,23,59,59) ).today?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,2,0) ).today?
+ Date.stub(:current, Date.new(2000, 1, 1)) do
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(1999,12,31,23,59,59) ).today?
+ assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,0) ).today?
+ assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,1,23,59,59) ).today?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.utc(2000,1,2,0) ).today?
+ end
end
def test_past_with_time_current_as_time_local
with_env_tz 'US/Eastern' do
- Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45))
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past?
+ Time.stub(:current, Time.local(2005,2,10,15,30,45)) do
+ assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past?
+ end
end
end
def test_past_with_time_current_as_time_with_zone
twz = ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45) )
- Time.stubs(:current).returns(twz)
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past?
+ Time.stub(:current, twz) do
+ assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).past?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).past?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).past?
+ end
end
def test_future_with_time_current_as_time_local
with_env_tz 'US/Eastern' do
- Time.stubs(:current).returns(Time.local(2005,2,10,15,30,45))
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future?
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future?
+ Time.stub(:current, Time.local(2005,2,10,15,30,45)) do
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future?
+ assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future?
+ end
end
end
def test_future_with_time_current_as_time_with_zone
twz = ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45) )
- Time.stubs(:current).returns(twz)
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future?
- assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future?
- assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future?
+ Time.stub(:current, twz) do
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,44)).future?
+ assert_equal false, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,45)).future?
+ assert_equal true, ActiveSupport::TimeWithZone.new( nil, @time_zone, Time.local(2005,2,10,15,30,46)).future?
+ end
end
def test_eql?
@@ -1033,19 +1038,21 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < ActiveSupport::TestCase
def test_current_returns_time_now_when_zone_not_set
with_env_tz 'US/Eastern' do
- Time.stubs(:now).returns Time.local(2000)
- assert_equal false, Time.current.is_a?(ActiveSupport::TimeWithZone)
- assert_equal Time.local(2000), Time.current
+ Time.stub(:now, Time.local(2000)) do
+ assert_equal false, Time.current.is_a?(ActiveSupport::TimeWithZone)
+ assert_equal Time.local(2000), Time.current
+ end
end
end
def test_current_returns_time_zone_now_when_zone_set
Time.zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
with_env_tz 'US/Eastern' do
- Time.stubs(:now).returns Time.local(2000)
- assert_equal true, Time.current.is_a?(ActiveSupport::TimeWithZone)
- assert_equal 'Eastern Time (US & Canada)', Time.current.time_zone.name
- assert_equal Time.utc(2000), Time.current.time
+ Time.stub(:now, Time.local(2000)) do
+ assert_equal true, Time.current.is_a?(ActiveSupport::TimeWithZone)
+ assert_equal 'Eastern Time (US & Canada)', Time.current.time_zone.name
+ assert_equal Time.utc(2000), Time.current.time
+ end
end
end
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index 4a1d90bfd6..6dce7560dd 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -187,7 +187,7 @@ class DependenciesTest < ActiveSupport::TestCase
assert_kind_of Module, A
assert_kind_of Class, A::B
assert_kind_of Class, A::C::D
- assert_kind_of Class, A::C::E::F
+ assert_kind_of Class, A::C::EM::F
end
end
@@ -552,24 +552,24 @@ class DependenciesTest < ActiveSupport::TestCase
def test_const_missing_in_anonymous_modules_loads_top_level_constants
with_autoloading_fixtures do
# class_eval STRING pushes the class to the nesting of the eval'ed code.
- klass = Class.new.class_eval "E"
- assert_equal E, klass
+ klass = Class.new.class_eval "EM"
+ assert_equal EM, klass
end
ensure
- remove_constants(:E)
+ remove_constants(:EM)
end
def test_const_missing_in_anonymous_modules_raises_if_the_constant_belongs_to_Object
with_autoloading_fixtures do
- require_dependency 'e'
+ require_dependency 'em'
mod = Module.new
- e = assert_raise(NameError) { mod::E }
- assert_equal 'E cannot be autoloaded from an anonymous class or module', e.message
- assert_equal :E, e.name
+ e = assert_raise(NameError) { mod::EM }
+ assert_equal 'EM cannot be autoloaded from an anonymous class or module', e.message
+ assert_equal :EM, e.name
end
ensure
- remove_constants(:E)
+ remove_constants(:EM)
end
def test_removal_from_tree_should_be_detected
@@ -664,19 +664,19 @@ class DependenciesTest < ActiveSupport::TestCase
def test_preexisting_constants_are_not_marked_as_autoloaded
with_autoloading_fixtures do
- require_dependency 'e'
- assert ActiveSupport::Dependencies.autoloaded?(:E)
+ require_dependency 'em'
+ assert ActiveSupport::Dependencies.autoloaded?(:EM)
ActiveSupport::Dependencies.clear
end
- Object.const_set :E, Class.new
+ Object.const_set :EM, Class.new
with_autoloading_fixtures do
- require_dependency 'e'
- assert ! ActiveSupport::Dependencies.autoloaded?(:E), "E shouldn't be marked autoloaded!"
+ require_dependency 'em'
+ assert ! ActiveSupport::Dependencies.autoloaded?(:EM), "EM shouldn't be marked autoloaded!"
ActiveSupport::Dependencies.clear
end
ensure
- remove_constants(:E)
+ remove_constants(:EM)
end
def test_constants_in_capitalized_nesting_marked_as_autoloaded
diff --git a/activesupport/test/ordered_options_test.rb b/activesupport/test/ordered_options_test.rb
index fdc745b23b..18767a3536 100644
--- a/activesupport/test/ordered_options_test.rb
+++ b/activesupport/test/ordered_options_test.rb
@@ -85,4 +85,19 @@ class OrderedOptionsTest < ActiveSupport::TestCase
assert_equal 42, a.method(:blah=).call(42)
assert_equal 42, a.method(:blah).call
end
+
+ def test_raises_with_bang
+ a = ActiveSupport::OrderedOptions.new
+ a[:foo] = :bar
+ assert a.respond_to?(:foo!)
+
+ assert_nothing_raised { a.foo! }
+ assert_equal a.foo, a.foo!
+
+ assert_raises(KeyError) do
+ a.foo = nil
+ a.foo!
+ end
+ assert_raises(KeyError) { a.non_existing_key! }
+ end
end
diff --git a/activesupport/test/time_travel_test.rb b/activesupport/test/time_travel_test.rb
index 676a143692..59c3e52c2f 100644
--- a/activesupport/test/time_travel_test.rb
+++ b/activesupport/test/time_travel_test.rb
@@ -1,83 +1,90 @@
require 'abstract_unit'
-require 'active_support/core_ext/date'
require 'active_support/core_ext/date_time'
require 'active_support/core_ext/numeric/time'
class TimeTravelTest < ActiveSupport::TestCase
- setup do
- Time.stubs now: Time.now
- end
-
teardown do
travel_back
end
def test_time_helper_travel
- expected_time = Time.now + 1.day
- travel 1.day
-
- assert_equal expected_time.to_s(:db), Time.now.to_s(:db)
- assert_equal expected_time.to_date, Date.today
- assert_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db)
- end
-
- def test_time_helper_travel_with_block
- expected_time = Time.now + 1.day
+ Time.stub(:now, Time.now) do
+ expected_time = Time.now + 1.day
+ travel 1.day
- travel 1.day do
assert_equal expected_time.to_s(:db), Time.now.to_s(:db)
assert_equal expected_time.to_date, Date.today
assert_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db)
end
-
- assert_not_equal expected_time.to_s(:db), Time.now.to_s(:db)
- assert_not_equal expected_time.to_date, Date.today
- assert_not_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db)
end
- def test_time_helper_travel_to
- expected_time = Time.new(2004, 11, 24, 01, 04, 44)
- travel_to expected_time
-
- assert_equal expected_time, Time.now
- assert_equal Date.new(2004, 11, 24), Date.today
- assert_equal expected_time.to_datetime, DateTime.now
+ def test_time_helper_travel_with_block
+ Time.stub(:now, Time.now) do
+ expected_time = Time.now + 1.day
+
+ travel 1.day do
+ assert_equal expected_time.to_s(:db), Time.now.to_s(:db)
+ assert_equal expected_time.to_date, Date.today
+ assert_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db)
+ end
+
+ assert_not_equal expected_time.to_s(:db), Time.now.to_s(:db)
+ assert_not_equal expected_time.to_date, Date.today
+ assert_not_equal expected_time.to_datetime.to_s(:db), DateTime.now.to_s(:db)
+ end
end
- def test_time_helper_travel_to_with_block
- expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+ def test_time_helper_travel_to
+ Time.stub(:now, Time.now) do
+ expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+ travel_to expected_time
- travel_to expected_time do
assert_equal expected_time, Time.now
assert_equal Date.new(2004, 11, 24), Date.today
assert_equal expected_time.to_datetime, DateTime.now
end
+ end
- assert_not_equal expected_time, Time.now
- assert_not_equal Date.new(2004, 11, 24), Date.today
- assert_not_equal expected_time.to_datetime, DateTime.now
+ def test_time_helper_travel_to_with_block
+ Time.stub(:now, Time.now) do
+ expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+
+ travel_to expected_time do
+ assert_equal expected_time, Time.now
+ assert_equal Date.new(2004, 11, 24), Date.today
+ assert_equal expected_time.to_datetime, DateTime.now
+ end
+
+ assert_not_equal expected_time, Time.now
+ assert_not_equal Date.new(2004, 11, 24), Date.today
+ assert_not_equal expected_time.to_datetime, DateTime.now
+ end
end
def test_time_helper_travel_back
- expected_time = Time.new(2004, 11, 24, 01, 04, 44)
+ Time.stub(:now, Time.now) do
+ expected_time = Time.new(2004, 11, 24, 01, 04, 44)
- travel_to expected_time
- assert_equal expected_time, Time.now
- assert_equal Date.new(2004, 11, 24), Date.today
- assert_equal expected_time.to_datetime, DateTime.now
- travel_back
+ travel_to expected_time
+ assert_equal expected_time, Time.now
+ assert_equal Date.new(2004, 11, 24), Date.today
+ assert_equal expected_time.to_datetime, DateTime.now
+ travel_back
- assert_not_equal expected_time, Time.now
- assert_not_equal Date.new(2004, 11, 24), Date.today
- assert_not_equal expected_time.to_datetime, DateTime.now
+ assert_not_equal expected_time, Time.now
+ assert_not_equal Date.new(2004, 11, 24), Date.today
+ assert_not_equal expected_time.to_datetime, DateTime.now
+ end
end
def test_travel_to_will_reset_the_usec_to_avoid_mysql_rouding
- travel_to Time.utc(2014, 10, 10, 10, 10, 50, 999999) do
- assert_equal 50, Time.now.sec
- assert_equal 0, Time.now.usec
- assert_equal 50, DateTime.now.sec
- assert_equal 0, DateTime.now.usec
+ Time.stub(:now, Time.now) do
+ travel_to Time.utc(2014, 10, 10, 10, 10, 50, 999999) do
+ assert_equal 50, Time.now.sec
+ assert_equal 0, Time.now.usec
+ assert_equal 50, DateTime.now.sec
+ assert_equal 0, DateTime.now.usec
+ end
end
end
end
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 5e0474f449..34bb0e2995 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -252,9 +252,10 @@ class TimeZoneTest < ActiveSupport::TestCase
def test_parse_with_incomplete_date
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
- zone.stubs(:now).returns zone.local(1999,12,31)
- twz = zone.parse('19:00:00')
- assert_equal Time.utc(1999,12,31,19), twz.time
+ zone.stub(:now, zone.local(1999,12,31)) do
+ twz = zone.parse('19:00:00')
+ assert_equal Time.utc(1999,12,31,19), twz.time
+ end
end
def test_parse_with_day_omitted
@@ -284,9 +285,10 @@ class TimeZoneTest < ActiveSupport::TestCase
def test_parse_with_missing_time_components
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
- zone.stubs(:now).returns zone.local(1999, 12, 31, 12, 59, 59)
- twz = zone.parse('2012-12-01')
- assert_equal Time.utc(2012, 12, 1), twz.time
+ zone.stub(:now, zone.local(1999, 12, 31, 12, 59, 59)) do
+ twz = zone.parse('2012-12-01')
+ assert_equal Time.utc(2012, 12, 1), twz.time
+ end
end
def test_parse_with_javascript_date
diff --git a/activesupport/test/xml_mini_test.rb b/activesupport/test/xml_mini_test.rb
index bcd6997b06..0e4e7427d2 100644
--- a/activesupport/test/xml_mini_test.rb
+++ b/activesupport/test/xml_mini_test.rb
@@ -1,7 +1,6 @@
require 'abstract_unit'
require 'active_support/xml_mini'
require 'active_support/builder'
-require 'active_support/core_ext/array'
require 'active_support/core_ext/hash'
require 'active_support/core_ext/big_decimal'
diff --git a/ci/travis.rb b/ci/travis.rb
index b62f90a59e..52fef05fbf 100755
--- a/ci/travis.rb
+++ b/ci/travis.rb
@@ -16,13 +16,14 @@ end
class Build
MAP = {
'railties' => 'railties',
- 'ap' => 'actionpack',
- 'am' => 'actionmailer',
- 'amo' => 'activemodel',
- 'as' => 'activesupport',
- 'ar' => 'activerecord',
- 'av' => 'actionview',
- 'aj' => 'activejob'
+ 'ap' => 'actionpack',
+ 'am' => 'actionmailer',
+ 'amo' => 'activemodel',
+ 'as' => 'activesupport',
+ 'ar' => 'activerecord',
+ 'av' => 'actionview',
+ 'aj' => 'activejob',
+ 'guides' => 'guides'
}
attr_reader :component, :options
@@ -36,7 +37,11 @@ class Build
self.options.update(options)
Dir.chdir(dir) do
announce(heading)
- rake(*tasks)
+ if guides?
+ run_bug_report_templates
+ else
+ rake(*tasks)
+ end
end
end
@@ -71,6 +76,10 @@ class Build
gem == 'activerecord'
end
+ def guides?
+ gem == 'guides'
+ end
+
def isolated?
options[:isolated]
end
@@ -96,6 +105,12 @@ class Build
end
true
end
+
+ def run_bug_report_templates
+ Dir.glob('bug_report_templates/*.rb').all? do |file|
+ system(Gem.ruby, '-w', file)
+ end
+ end
end
if ENV['GEM']=='aj:integration'
@@ -110,6 +125,7 @@ ENV['GEM'].split(',').each do |gem|
next if ENV['TRAVIS_PULL_REQUEST'] && ENV['TRAVIS_PULL_REQUEST'] != 'false' && isolated
next if gem == 'railties' && isolated
next if gem == 'aj:integration' && isolated
+ next if gem == 'guides' && isolated
build = Build.new(gem, :isolated => isolated)
results[build.key] = build.run!
diff --git a/guides/bug_report_templates/action_controller_gem.rb b/guides/bug_report_templates/action_controller_gem.rb
index 032e6bfe11..11561c55f9 100644
--- a/guides/bug_report_templates/action_controller_gem.rb
+++ b/guides/bug_report_templates/action_controller_gem.rb
@@ -1,7 +1,16 @@
-# Activate the gem you are reporting the issue against.
-gem 'rails', '4.2.0'
+begin
+ require 'bundler/inline'
+rescue LoadError => e
+ $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
+ raise e
+end
+
+gemfile(true) do
+ source 'https://rubygems.org'
+ # Activate the gem you are reporting the issue against.
+ gem 'rails', '4.2.0'
+end
-require 'rails'
require 'rack/test'
require 'action_controller/railtie'
diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb
index 9be8130884..66887398b9 100644
--- a/guides/bug_report_templates/action_controller_master.rb
+++ b/guides/bug_report_templates/action_controller_master.rb
@@ -1,17 +1,16 @@
-unless File.exist?('Gemfile')
- File.write('Gemfile', <<-GEMFILE)
- source 'https://rubygems.org'
- gem 'rails', github: 'rails/rails'
- gem 'arel', github: 'rails/arel'
- GEMFILE
-
- system 'bundle'
+begin
+ require 'bundler/inline'
+rescue LoadError => e
+ $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
+ raise e
end
-require 'bundler'
-Bundler.setup(:default)
+gemfile(true) do
+ source 'https://rubygems.org'
+ gem 'rails', github: 'rails/rails'
+ gem 'arel', github: 'rails/arel'
+end
-require 'rails'
require 'action_controller/railtie'
class TestApp < Rails::Application
diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb
index b295d9d21f..09d6e7b331 100644
--- a/guides/bug_report_templates/active_record_gem.rb
+++ b/guides/bug_report_templates/active_record_gem.rb
@@ -1,5 +1,17 @@
-# Activate the gem you are reporting the issue against.
-gem 'activerecord', '4.2.0'
+begin
+ require 'bundler/inline'
+rescue LoadError => e
+ $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
+ raise e
+end
+
+gemfile(true) do
+ source 'https://rubygems.org'
+ # Activate the gem you are reporting the issue against.
+ gem 'activerecord', '4.2.0'
+ gem 'sqlite3'
+end
+
require 'active_record'
require 'minitest/autorun'
require 'logger'
diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb
index 9557f0b7c5..270dbe7df7 100644
--- a/guides/bug_report_templates/active_record_master.rb
+++ b/guides/bug_report_templates/active_record_master.rb
@@ -1,16 +1,16 @@
-unless File.exist?('Gemfile')
- File.write('Gemfile', <<-GEMFILE)
- source 'https://rubygems.org'
- gem 'rails', github: 'rails/rails'
- gem 'arel', github: 'rails/arel'
- gem 'sqlite3'
- GEMFILE
-
- system 'bundle'
+begin
+ require 'bundler/inline'
+rescue LoadError => e
+ $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
+ raise e
end
-require 'bundler'
-Bundler.setup(:default)
+gemfile(true) do
+ source 'https://rubygems.org'
+ gem 'rails', github: 'rails/rails'
+ gem 'arel', github: 'rails/arel'
+ gem 'sqlite3'
+end
require 'active_record'
require 'minitest/autorun'
diff --git a/guides/bug_report_templates/generic_gem.rb b/guides/bug_report_templates/generic_gem.rb
index 3e3f8593d4..a4fe51156d 100644
--- a/guides/bug_report_templates/generic_gem.rb
+++ b/guides/bug_report_templates/generic_gem.rb
@@ -1,6 +1,16 @@
-# Activate the gems you are reporting the issue against.
-gem 'activesupport', '4.2.0'
-require 'active_support'
+begin
+ require 'bundler/inline'
+rescue LoadError => e
+ $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
+ raise e
+end
+
+gemfile(true) do
+ source 'https://rubygems.org'
+ # Activate the gem you are reporting the issue against.
+ gem 'activesupport', '4.2.0'
+end
+
require 'active_support/core_ext/object/blank'
require 'minitest/autorun'
diff --git a/guides/bug_report_templates/generic_master.rb b/guides/bug_report_templates/generic_master.rb
index d930482d4e..b6b4562751 100644
--- a/guides/bug_report_templates/generic_master.rb
+++ b/guides/bug_report_templates/generic_master.rb
@@ -1,15 +1,15 @@
-unless File.exist?('Gemfile')
- File.write('Gemfile', <<-GEMFILE)
- source 'https://rubygems.org'
- gem 'rails', github: 'rails/rails'
- gem 'arel', github: 'rails/arel'
- GEMFILE
-
- system 'bundle'
+begin
+ require 'bundler/inline'
+rescue LoadError => e
+ $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
+ raise e
end
-require 'bundler'
-Bundler.setup(:default)
+gemfile(true) do
+ source 'https://rubygems.org'
+ gem 'rails', github: 'rails/rails'
+ gem 'arel', github: 'rails/arel'
+end
require 'active_support'
require 'active_support/core_ext/object/blank'
diff --git a/guides/source/3_1_release_notes.md b/guides/source/3_1_release_notes.md
index 537aa5a371..d753346fa3 100644
--- a/guides/source/3_1_release_notes.md
+++ b/guides/source/3_1_release_notes.md
@@ -199,7 +199,7 @@ Railties
* jQuery is the new default JavaScript library.
-* jQuery and Prototype are no longer vendored and is provided from now on by the jquery-rails and prototype-rails gems.
+* jQuery and Prototype are no longer vendored and is provided from now on by the `jquery-rails` and `prototype-rails` gems.
* The application generator accepts an option `-j` which can be an arbitrary string. If passed "foo", the gem "foo-rails" is added to the `Gemfile`, and the application JavaScript manifest requires "foo" and "foo_ujs". Currently only "prototype-rails" and "jquery-rails" exist and provide those files via the asset pipeline.
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index 089ce53f07..bf3bf5d19e 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -344,7 +344,7 @@ The same format can be used to set carbon copy (Cc:) and blind carbon copy
Sometimes you wish to show the name of the person instead of just their email
address when they receive the email. The trick to doing that is to format the
-email address in the format `"Full Name <email>"`.
+email address in the format `"Full Name" <email>`.
```ruby
def welcome_email(user)
@@ -503,7 +503,7 @@ You will need to use:
By using the full URL, your links will now work in your emails.
-#### generating URLs with `url_for`
+#### Generating URLs with `url_for`
`url_for` generate full URL by default in templates.
@@ -517,7 +517,7 @@ If you did not configure the `:host` option globally make sure to pass it to
action: 'greeting') %>
```
-#### generating URLs with named routes
+#### Generating URLs with Named Routes
Email clients have no web context and so paths have no base URL to form complete
web addresses. Thus, you should always use the "_url" variant of named route
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index 3541bbaa93..09fac41491 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -214,7 +214,8 @@ By default `ActionView::Partials::PartialRenderer` has its object in a local var
<%= render partial: "product" %>
```
-within product we'll get `@product` in the local variable `product`, as if we had written:
+within `_product` partial we'll get `@product` in the local variable `product`,
+as if we had written:
```erb
<%= render partial: "product", locals: { product: @product } %>
@@ -317,26 +318,6 @@ The `box` layout simply wraps the `_article` partial in a `div`:
</div>
```
-The `_article` partial wraps the article's `body` in a `div` with the `id` of the article using the `div_for` helper:
-
-**articles/_article.html.erb**
-
-```html+erb
-<%= div_for(article) do %>
- <p><%= article.body %></p>
-<% end %>
-```
-
-this would output the following:
-
-```html
-<div class='box'>
- <div id='article_1'>
- <p>Partial Layouts are cool!</p>
- </div>
-</div>
-```
-
Note that the partial layout has access to the local `article` variable that was passed into the `render` call. However, unlike application-wide layouts, partial layouts still have the underscore prefix.
You can also render a block of code within a partial layout instead of calling `yield`. For example, if we didn't have the `_article` partial, we could do this instead:
@@ -345,9 +326,9 @@ You can also render a block of code within a partial layout instead of calling `
```html+erb
<% render(layout: 'box', locals: { article: @article }) do %>
- <%= div_for(article) do %>
+ <div>
<p><%= article.body %></p>
- <% end %>
+ </div>
<% end %>
```
@@ -552,7 +533,7 @@ end
```ruby
atom_feed do |feed|
feed.title("Articles Index")
- feed.updated((@articles.first.created_at))
+ feed.updated(@articles.first.created_at)
@articles.each do |article|
feed.entry(article) do |entry|
@@ -726,7 +707,7 @@ Returns a select tag with options for each of the minutes 0 through 59 with the
```ruby
# Generates a select field for minutes that defaults to the minutes for the time provided.
-select_minute(Time.now + 6.hours)
+select_minute(Time.now + 10.minutes)
```
#### select_month
diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md
index 6551ba0389..a227b54040 100644
--- a/guides/source/active_record_basics.md
+++ b/guides/source/active_record_basics.md
@@ -74,8 +74,8 @@ By default, Active Record uses some naming conventions to find out how the
mapping between models and database tables should be created. Rails will
pluralize your class names to find the respective database table. So, for
a class `Book`, you should have a database table called **books**. The Rails
-pluralization mechanisms are very powerful, being capable to pluralize (and
-singularize) both regular and irregular words. When using class names composed
+pluralization mechanisms are very powerful, being capable of pluralizing (and
+singularizing) both regular and irregular words. When using class names composed
of two or more words, the model class name should follow the Ruby conventions,
using the CamelCase form, while the table name must contain the words separated
by underscores. Examples:
diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md
index 7a994cc5de..80b1bde1c7 100644
--- a/guides/source/active_record_migrations.md
+++ b/guides/source/active_record_migrations.md
@@ -554,7 +554,7 @@ or write the `up` and `down` methods instead of using the `change` method.
Complex migrations may require processing that Active Record doesn't know how
to reverse. You can use `reversible` to specify what to do when running a
-migration what else to do when reverting it. For example:
+migration and what else to do when reverting it. For example:
```ruby
class ExampleMigration < ActiveRecord::Migration
@@ -606,7 +606,7 @@ schema, and the `down` method of your migration should revert the
transformations done by the `up` method. In other words, the database schema
should be unchanged if you do an `up` followed by a `down`. For example, if you
create a table in the `up` method, you should drop it in the `down` method. It
-is wise to reverse the transformations in precisely the reverse order they were
+is wise to perform the transformations in precisely the reverse order they were
made in the `up` method. The example in the `reversible` section is equivalent to:
```ruby
diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md
index 5e58d5baeb..dcc523eb0f 100644
--- a/guides/source/active_record_postgresql.md
+++ b/guides/source/active_record_postgresql.md
@@ -29,8 +29,8 @@ that are supported by the PostgreSQL adapter.
### Bytea
-* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-binary.html)
-* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-binarystring.html)
+* [type definition](http://www.postgresql.org/docs/current/static/datatype-binary.html)
+* [functions and operators](http://www.postgresql.org/docs/current/static/functions-binarystring.html)
```ruby
# db/migrate/20140207133952_create_documents.rb
@@ -49,8 +49,8 @@ Document.create payload: data
### Array
-* [type definition](http://www.postgresql.org/docs/9.3/static/arrays.html)
-* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-array.html)
+* [type definition](http://www.postgresql.org/docs/current/static/arrays.html)
+* [functions and operators](http://www.postgresql.org/docs/current/static/functions-array.html)
```ruby
# db/migrate/20140207133952_create_books.rb
@@ -83,7 +83,7 @@ Book.where("array_length(ratings, 1) >= 3")
### Hstore
-* [type definition](http://www.postgresql.org/docs/9.3/static/hstore.html)
+* [type definition](http://www.postgresql.org/docs/current/static/hstore.html)
NOTE: you need to enable the `hstore` extension to use hstore.
@@ -112,8 +112,8 @@ profile.save!
### JSON
-* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-json.html)
-* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-json.html)
+* [type definition](http://www.postgresql.org/docs/current/static/datatype-json.html)
+* [functions and operators](http://www.postgresql.org/docs/current/static/functions-json.html)
```ruby
# db/migrate/20131220144913_create_events.rb
@@ -138,10 +138,10 @@ Event.where("payload->>'kind' = ?", "user_renamed")
### Range Types
-* [type definition](http://www.postgresql.org/docs/9.3/static/rangetypes.html)
-* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-range.html)
+* [type definition](http://www.postgresql.org/docs/current/static/rangetypes.html)
+* [functions and operators](http://www.postgresql.org/docs/current/static/functions-range.html)
-This type is mapped to Ruby [`Range`](http://www.ruby-doc.org/core-2.1.1/Range.html) objects.
+This type is mapped to Ruby [`Range`](http://www.ruby-doc.org/core-2.2.2/Range.html) objects.
```ruby
# db/migrate/20130923065404_create_events.rb
@@ -173,7 +173,7 @@ event.ends_at # => Thu, 13 Feb 2014
### Composite Types
-* [type definition](http://www.postgresql.org/docs/9.3/static/rowtypes.html)
+* [type definition](http://www.postgresql.org/docs/current/static/rowtypes.html)
Currently there is no special support for composite types. They are mapped to
normal text columns:
@@ -213,7 +213,7 @@ contact.save!
### Enumerated Types
-* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-enum.html)
+* [type definition](http://www.postgresql.org/docs/current/static/datatype-enum.html)
Currently there is no special support for enumerated types. They are mapped as
normal text columns:
@@ -242,9 +242,9 @@ article.save!
### UUID
-* [type definition](http://www.postgresql.org/docs/9.4/static/datatype-uuid.html)
-* [pgcrypto generator function](http://www.postgresql.org/docs/9.4/static/pgcrypto.html#AEN159361)
-* [uuid-ossp generator functions](http://www.postgresql.org/docs/9.4/static/uuid-ossp.html)
+* [type definition](http://www.postgresql.org/docs/current/static/datatype-uuid.html)
+* [pgcrypto generator function](http://www.postgresql.org/docs/current/static/pgcrypto.html#AEN159361)
+* [uuid-ossp generator functions](http://www.postgresql.org/docs/current/static/uuid-ossp.html)
NOTE: you need to enable the `pgcrypto` (only PostgreSQL >= 9.4) or `uuid-ossp`
extension to use uuid.
@@ -290,8 +290,8 @@ end
### Bit String Types
-* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-bit.html)
-* [functions and operators](http://www.postgresql.org/docs/9.3/static/functions-bitstring.html)
+* [type definition](http://www.postgresql.org/docs/current/static/datatype-bit.html)
+* [functions and operators](http://www.postgresql.org/docs/current/static/functions-bitstring.html)
```ruby
# db/migrate/20131220144913_create_users.rb
@@ -314,10 +314,10 @@ user.save!
### Network Address Types
-* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-net-types.html)
+* [type definition](http://www.postgresql.org/docs/current/static/datatype-net-types.html)
The types `inet` and `cidr` are mapped to Ruby
-[`IPAddr`](http://www.ruby-doc.org/stdlib-2.1.1/libdoc/ipaddr/rdoc/IPAddr.html)
+[`IPAddr`](http://www.ruby-doc.org/stdlib-2.2.2/libdoc/ipaddr/rdoc/IPAddr.html)
objects. The `macaddr` type is mapped to normal text.
```ruby
@@ -349,7 +349,7 @@ macbook.address
### Geometric Types
-* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-geometric.html)
+* [type definition](http://www.postgresql.org/docs/current/static/datatype-geometric.html)
All geometric types, with the exception of `points` are mapped to normal text.
A point is casted to an array containing `x` and `y` coordinates.
@@ -404,7 +404,7 @@ Document.where("to_tsvector('english', title || ' ' || body) @@ to_tsquery(?)",
Database Views
--------------
-* [view creation](http://www.postgresql.org/docs/9.3/static/sql-createview.html)
+* [view creation](http://www.postgresql.org/docs/current/static/sql-createview.html)
Imagine you need to work with a legacy database containing the following table:
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 2f10bc4e7c..e3cfabb327 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -898,7 +898,7 @@ For example:
Item.transaction do
i = Item.lock.first
i.name = 'Jones'
- i.save
+ i.save!
end
```
@@ -1787,8 +1787,9 @@ EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `articles` ON `articles`.`
under MySQL.
-Active Record performs a pretty printing that emulates the one of the database
-shells. So, the same query running with the PostgreSQL adapter would yield instead
+Active Record performs a pretty printing that emulates that of the
+corresponding database shell. So, the same query running with the
+PostgreSQL adapter would yield instead
```
EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "articles" ON "articles"."user_id" = "users"."id" WHERE "users"."id" = 1
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index 343b761e93..7932853c11 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -47,7 +47,7 @@ built-in helpers for common needs, and allows you to create your own validation
methods as well.
There are several other ways to validate data before it is saved into your
-database, including native database constraints, client-side validations,
+database, including native database constraints, client-side validations and
controller-level validations. Here's a summary of the pros and cons:
* Database constraints and/or stored procedures make the validation mechanisms
@@ -122,7 +122,7 @@ database only if the object is valid:
* `update!`
The bang versions (e.g. `save!`) raise an exception if the record is invalid.
-The non-bang versions don't, `save` and `update` return `false`,
+The non-bang versions don't: `save` and `update` return `false`, and
`create` just returns the object.
### Skipping Validations
@@ -143,7 +143,7 @@ database regardless of its validity. They should be used with caution.
* `update_counters`
Note that `save` also has the ability to skip validations if passed `validate:
-false` as argument. This technique should be used with caution.
+false` as an argument. This technique should be used with caution.
* `save(validate: false)`
@@ -272,7 +272,7 @@ available helpers.
This method validates that a checkbox on the user interface was checked when a
form was submitted. This is typically used when the user needs to agree to your
-application's terms of service, confirm reading some text, or any similar
+application's terms of service, confirm that some text is read, or any similar
concept. This validation is very specific to web applications and this
'acceptance' does not need to be recorded anywhere in your database (if you
don't have a field for it, the helper will just create a virtual attribute).
@@ -283,6 +283,7 @@ class Person < ActiveRecord::Base
end
```
+This check is performed only if `terms_of_service` is not `nil`.
The default error message for this helper is _"must be accepted"_.
It can receive an `:accept` option, which determines the value that will be
@@ -338,7 +339,7 @@ In your view template you could use something like
This check is performed only if `email_confirmation` is not `nil`. To require
confirmation, make sure to add a presence check for the confirmation attribute
-(we'll take a look at `presence` later on this guide):
+(we'll take a look at `presence` later on in this guide):
```ruby
class Person < ActiveRecord::Base
@@ -499,9 +500,9 @@ constraints to acceptable values:
default error message for this option is _"must be equal to %{count}"_.
* `:less_than` - Specifies the value must be less than the supplied value. The
default error message for this option is _"must be less than %{count}"_.
-* `:less_than_or_equal_to` - Specifies the value must be less than or equal the
- supplied value. The default error message for this option is _"must be less
- than or equal to %{count}"_.
+* `:less_than_or_equal_to` - Specifies the value must be less than or equal to
+ the supplied value. The default error message for this option is _"must be
+ less than or equal to %{count}"_.
* `:odd` - Specifies the value must be an odd number if set to true. The
default error message for this option is _"must be odd"_.
* `:even` - Specifies the value must be an even number if set to true. The
@@ -551,7 +552,6 @@ Since `false.blank?` is true, if you want to validate the presence of a boolean
field you should use one of the following validations:
```ruby
-validates :boolean_field_name, presence: true
validates :boolean_field_name, inclusion: { in: [true, false] }
validates :boolean_field_name, exclusion: { in: [nil] }
```
@@ -626,7 +626,7 @@ class Holiday < ActiveRecord::Base
message: "should happen once per year" }
end
```
-Should you wish to create a database constraint to prevent possible violations of a uniqueness validation using the `:scope` option, you must create a unique index on both columns in your database. See [the MySQL manual](http://dev.mysql.com/doc/refman/5.6/en/multiple-column-indexes.html) for more details about multiple column indexes or [the PostgreSQL manual](http://www.postgresql.org/docs/9.4/static/ddl-constraints.html) for examples of unique constraints that refer to a group of columns.
+Should you wish to create a database constraint to prevent possible violations of a uniqueness validation using the `:scope` option, you must create a unique index on both columns in your database. See [the MySQL manual](http://dev.mysql.com/doc/refman/5.6/en/multiple-column-indexes.html) for more details about multiple column indexes or [the PostgreSQL manual](http://www.postgresql.org/docs/current/static/ddl-constraints.html) for examples of unique constraints that refer to a group of columns.
There is also a `:case_sensitive` option that you can use to define whether the
uniqueness constraint will be case sensitive or not. This option defaults to
@@ -813,7 +813,7 @@ end
Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank
```
-There is also an ability to pass custom exception to `:strict` option.
+There is also the ability to pass a custom exception to the `:strict` option.
```ruby
class Person < ActiveRecord::Base
@@ -877,7 +877,7 @@ end
### Grouping Conditional validations
-Sometimes it is useful to have multiple validations use one condition, it can
+Sometimes it is useful to have multiple validations use one condition. It can
be easily achieved using `with_options`.
```ruby
@@ -889,8 +889,8 @@ class User < ActiveRecord::Base
end
```
-All validations inside of `with_options` block will have automatically passed
-the condition `if: :is_admin?`
+All validations inside of the `with_options` block will have automatically
+passed the condition `if: :is_admin?`
### Combining Validation Conditions
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 2a643680f7..01bf928407 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -1936,6 +1936,8 @@ as well as adding or subtracting their results from a Time object. For example:
(4.months + 5.years).from_now
```
+NOTE: Defined in `active_support/core_ext/numeric/time.rb`
+
### Formatting
Enables the formatting of numbers in a variety of ways.
@@ -2193,6 +2195,16 @@ removed:
NOTE: Defined in `active_support/core_ext/enumerable.rb`.
+### `pluck`
+
+The method `pluck` returns an array based on the given key:
+
+```ruby
+[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name) # => ["David", "Rafael", "Aaron"]
+```
+
+NOTE: Defined in `active_support/core_ext/enumerable.rb`.
+
Extensions to `Array`
---------------------
@@ -2201,14 +2213,14 @@ Extensions to `Array`
Active Support augments the API of arrays to ease certain ways of accessing them. For example, `to` returns the subarray of elements up to the one at the passed index:
```ruby
-%w(a b c d).to(2) # => %w(a b c)
+%w(a b c d).to(2) # => ["a", "b", "c"]
[].to(7) # => []
```
Similarly, `from` returns the tail from the element at the passed index to the end. If the index is greater than the length of the array, it returns an empty array.
```ruby
-%w(a b c d).from(2) # => %w(c d)
+%w(a b c d).from(2) # => ["c", "d"]
%w(a b c d).from(10) # => []
[].from(0) # => []
```
@@ -2216,7 +2228,7 @@ Similarly, `from` returns the tail from the element at the passed index to the e
The methods `second`, `third`, `fourth`, and `fifth` return the corresponding element (`first` is built-in). Thanks to social wisdom and positive constructiveness all around, `forty_two` is also available.
```ruby
-%w(a b c d).third # => c
+%w(a b c d).third # => "c"
%w(a b c d).fifth # => nil
```
@@ -2229,7 +2241,7 @@ NOTE: Defined in `active_support/core_ext/array/access.rb`.
This method is an alias of `Array#unshift`.
```ruby
-%w(a b c d).prepend('e') # => %w(e a b c d)
+%w(a b c d).prepend('e') # => ["e", "a", "b", "c", "d"]
[].prepend(10) # => [10]
```
@@ -2240,8 +2252,8 @@ NOTE: Defined in `active_support/core_ext/array/prepend_and_append.rb`.
This method is an alias of `Array#<<`.
```ruby
-%w(a b c d).append('e') # => %w(a b c d e)
-[].append([1,2]) # => [[1,2]]
+%w(a b c d).append('e') # => ["a", "b", "c", "d", "e"]
+[].append([1,2]) # => [[1, 2]]
```
NOTE: Defined in `active_support/core_ext/array/prepend_and_append.rb`.
@@ -2465,7 +2477,7 @@ NOTE: Defined in `active_support/core_ext/array/wrap.rb`.
### Duplicating
-The method `Array.deep_dup` duplicates itself and all objects inside
+The method `Array#deep_dup` duplicates itself and all objects inside
recursively with Active Support method `Object#deep_dup`. It works like `Array#map` with sending `deep_dup` method to each object inside.
```ruby
@@ -2687,7 +2699,7 @@ NOTE: Defined in `active_support/core_ext/hash/deep_merge.rb`.
### Deep duplicating
-The method `Hash.deep_dup` duplicates itself and all keys and values
+The method `Hash#deep_dup` duplicates itself and all keys and values
inside recursively with Active Support method `Object#deep_dup`. It works like `Enumerator#each_with_object` with sending `deep_dup` method to each pair inside.
```ruby
@@ -2883,7 +2895,7 @@ The method `transform_values` accepts a block and returns a hash that has applie
```
There's also the bang variant `transform_values!` that applies the block operations to values in the very receiver.
-NOTE: Defined in `active_support/core_text/hash/transform_values.rb`.
+NOTE: Defined in `active_support/core_ext/hash/transform_values.rb`.
### Slicing
diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md
index 1b14bedfbf..373dbbb9aa 100644
--- a/guides/source/active_support_instrumentation.md
+++ b/guides/source/active_support_instrumentation.md
@@ -252,6 +252,20 @@ INFO. The adapters will add their own data as well.
| `:name` | Record's class |
| `:connection_id` | `self.object_id` |
+### instantiation.active_record
+
+| Key | Value |
+| ---------------- | ----------------------------------------- |
+| `:record_count` | Number of records that instantiated |
+| `:class_name` | Record's class |
+
+```ruby
+{
+ record_count: 1,
+ class_name: "User"
+}
+```
+
Action Mailer
-------------
diff --git a/guides/source/api_app.md b/guides/source/api_app.md
new file mode 100644
index 0000000000..0a6335ed88
--- /dev/null
+++ b/guides/source/api_app.md
@@ -0,0 +1,435 @@
+Using Rails for API-only Apps
+=============================
+
+In this guide you will learn:
+
+- What Rails provides for API-only applications
+- How to configure Rails to start without any browser features
+- How to decide which middlewares you will want to include
+- How to decide which modules to use in your controller
+
+endprologue.
+
+### What is an API app?
+
+Traditionally, when people said that they used Rails as an “API”, they
+meant providing a programmatically accessible API alongside their web
+application.\
+For example, GitHub provides [an API](http://developer.github.com) that
+you can use from your own custom clients.
+
+With the advent of client-side frameworks, more developers are using
+Rails to build a backend that is shared between their web application
+and other native applications.
+
+For example, Twitter uses its [public API](https://dev.twitter.com) in
+its web application, which is built as a static site that consumes JSON
+resources.
+
+Instead of using Rails to generate dynamic HTML that will communicate
+with the server through forms and links, many developers are treating
+their web application as just another client, delivered as static HTML,
+CSS and JavaScript, and consuming a simple JSON API
+
+This guide covers building a Rails application that serves JSON
+resources to an API client **or** client-side framework.
+
+### Why use Rails for JSON APIs?
+
+The first question a lot of people have when thinking about building a
+JSON API using Rails is: “isn’t using Rails to spit out some JSON
+overkill? Shouldn’t I just use something like Sinatra?”
+
+For very simple APIs, this may be true. However, even in very HTML-heavy
+applications, most of an application’s logic is actually outside of the
+view layer.
+
+The reason most people use Rails is that it provides a set of defaults
+that allows us to get up and running quickly without having to make a
+lot of trivial decisions.
+
+Let’s take a look at some of the things that Rails provides out of the
+box that are still applicable to API applications.
+
+Handled at the middleware layer:
+
+- Reloading: Rails applications support transparent reloading. This
+ works even if your application gets big and restarting the server
+ for every request becomes non-viable.
+- Development Mode: Rails application come with smart defaults for
+ development, making development pleasant without compromising
+ production-time performance.
+- Test Mode: Ditto test mode.
+- Logging: Rails applications log every request, with a level of
+ verbosity appropriate for the current mode. Rails logs in
+ development include information about the request environment,
+ database queries, and basic performance information.
+- Security: Rails detects and thwarts [IP spoofing
+ attacks](http://en.wikipedia.org/wiki/IP_address_spoofing) and
+ handles cryptographic signatures in a [timing
+ attack](http://en.wikipedia.org/wiki/Timing_attack) aware way. Don’t
+ know what an IP spoofing attack or a timing attack is? Exactly.
+- Parameter Parsing: Want to specify your parameters as JSON instead
+ of as a URL-encoded String? No problem. Rails will decode the JSON
+ for you and make it available in *params*. Want to use nested
+ URL-encoded params? That works too.
+- Conditional GETs: Rails handles conditional *GET*, (*ETag* and
+ *Last-Modified*), processing request headers and returning the
+ correct response headers and status code. All you need to do is use
+ the
+ [stale?](http://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-stale-3F)
+ check in your controller, and Rails will handle all of the HTTP
+ details for you.
+- Caching: If you use *dirty?* with public cache control, Rails will
+ automatically cache your responses. You can easily configure the
+ cache store.
+- HEAD requests: Rails will transparently convert *HEAD* requests into
+ *GET* requests, and return just the headers on the way out. This
+ makes *HEAD* work reliably in all Rails APIs.
+
+While you could obviously build these up in terms of existing Rack
+middlewares, I think this list demonstrates that the default Rails
+middleware stack provides a lot of value, even if you’re “just
+generating JSON”.
+
+Handled at the ActionPack layer:
+
+- Resourceful Routing: If you’re building a RESTful JSON API, you want
+ to be using the Rails router. Clean and conventional mapping from
+ HTTP to controllers means not having to spend time thinking about
+ how to model your API in terms of HTTP.
+- URL Generation: The flip side of routing is URL generation. A good
+ API based on HTTP includes URLs (see [the GitHub gist
+ API](http://developer.github.com/v3/gists/) for an example).
+- Header and Redirection Responses: *head :no\_content* and
+ *redirect\_to user\_url(current\_user)* come in handy. Sure, you
+ could manually add the response headers, but why?
+- Caching: Rails provides page, action and fragment caching. Fragment
+ caching is especially helpful when building up a nested JSON object.
+- Basic, Digest and Token Authentication: Rails comes with
+ out-of-the-box support for three kinds of HTTP authentication.
+- Instrumentation: Rails 3.0 added an instrumentation API that will
+ trigger registered handlers for a variety of events, such as action
+ processing, sending a file or data, redirection, and database
+ queries. The payload of each event comes with relevant information
+ (for the action processing event, the payload includes the
+ controller, action, params, request format, request method and the
+ request’s full path).
+- Generators: This may be passé for advanced Rails users, but it can
+ be nice to generate a resource and get your model, controller, test
+ stubs, and routes created for you in a single command.
+- Plugins: Many third-party libraries come with support for Rails that
+ reduces or eliminates the cost of setting up and gluing together the
+ library and the web framework. This includes things like overriding
+ default generators, adding rake tasks, and honoring Rails choices
+ (like the logger and cache backend).
+
+Of course, the Rails boot process also glues together all registered
+components. For example, the Rails boot process is what uses your
+*config/database.yml* file when configuring ActiveRecord.
+
+**The short version is**: you may not have thought about which parts of
+Rails are still applicable even if you remove the view layer, but the
+answer turns out to be “most of it”.
+
+### The Basic Configuration
+
+If you’re building a Rails application that will be an API server first
+and foremost, you can start with a more limited subset of Rails and add
+in features as needed.
+
+You can generate a new api Rails app:
+
+<shell>\
+\$ rails new my\_api --api\
+</shell>
+
+This will do three main things for you:
+
+- Configure your application to start with a more limited set of
+ middleware than normal. Specifically, it will not include any
+ middleware primarily useful for browser applications (like cookie
+ support) by default.
+- Make *ApplicationController* inherit from *ActionController::API*
+ instead of *ActionController::Base*. As with middleware, this will
+ leave out any *ActionController* modules that provide functionality
+ primarily used by browser applications.
+- Configure the generators to skip generating views, helpers and
+ assets when you generate a new resource.
+
+If you want to take an existing app and make it an API app, follow the
+following steps.
+
+In *config/application.rb* add the following line at the top of the
+*Application* class:
+
+<ruby>\
+config.api\_only!\
+</ruby>
+
+Change *app/controllers/application\_controller.rb*:
+
+<ruby>
+
+1. instead of\
+ class ApplicationController \< ActionController::Base\
+ end
+
+<!-- -->
+
+1. do\
+ class ApplicationController \< ActionController::API\
+ end\
+ </ruby>
+
+### Choosing Middlewares
+
+An API application comes with the following middlewares by default.
+
+- *Rack::Cache*: Caches responses with public *Cache-Control* headers
+ using HTTP caching semantics. See below for more information.
+- *Rack::Sendfile*: Uses a front-end server’s file serving support
+ from your Rails application.
+- *Rack::Lock*: If your application is not marked as threadsafe
+ (*config.threadsafe!*), this middleware will add a mutex around your
+ requests.
+- *ActionDispatch::RequestId*:
+- *Rails::Rack::Logger*:
+- *Rack::Runtime*: Adds a header to the response listing the total
+ runtime of the request.
+- *ActionDispatch::ShowExceptions*: Rescue exceptions and re-dispatch
+ them to an exception handling application
+- *ActionDispatch::DebugExceptions*: Log exceptions
+- *ActionDispatch::RemoteIp*: Protect against IP spoofing attacks
+- *ActionDispatch::Reloader*: In development mode, support code
+ reloading.
+- *ActionDispatch::ParamsParser*: Parse XML, YAML and JSON parameters
+ when the request’s *Content-Type* is one of those.
+- *ActionDispatch::Head*: Dispatch *HEAD* requests as *GET* requests,
+ and return only the status code and headers.
+- *Rack::ConditionalGet*: Supports the *stale?* feature in Rails
+ controllers.
+- *Rack::ETag*: Automatically set an *ETag* on all string responses.
+ This means that if the same response is returned from a controller
+ for the same URL, the server will return a *304 Not Modified*, even
+ if no additional caching steps are taken. This is primarily a
+ client-side optimization; it reduces bandwidth costs but not server
+ processing time.
+
+Other plugins, including *ActiveRecord*, may add additional middlewares.
+In general, these middlewares are agnostic to the type of app you are
+building, and make sense in an API-only Rails application.
+
+You can get a list of all middlewares in your application via:
+
+<shell>\
+\$ rake middleware\
+</shell>
+
+#### Using Rack::Cache
+
+When used with Rails, *Rack::Cache* uses the Rails cache store for its
+entity and meta stores. This means that if you use memcache, for your
+Rails app, for instance, the built-in HTTP cache will use memcache.
+
+To make use of *Rack::Cache*, you will want to use *stale?* in your
+controller. Here’s an example of *stale?* in use.
+
+<ruby>\
+def show\
+ @post = Post.find(params[:id])
+
+if stale?(:last\_modified =\> `post.updated_at)
+ render json: `post\
+ end\
+end\
+</ruby>
+
+The call to *stale?* will compare the *If-Modified-Since* header in the
+request with *@post.updated\_at*. If the header is newer than the last
+modified, this action will return a *304 Not Modified* response.
+Otherwise, it will render the response and include a *Last-Modified*
+header with the response.
+
+Normally, this mechanism is used on a per-client basis. *Rack::Cache*
+allows us to share this caching mechanism across clients. We can enable
+cross-client caching in the call to *stale?*
+
+<ruby>\
+def show\
+ @post = Post.find(params[:id])
+
+if stale?(:last\_modified =\> `post.updated_at, :public => true)
+ render json: `post\
+ end\
+end\
+</ruby>
+
+This means that *Rack::Cache* will store off *Last-Modified* value for a
+URL in the Rails cache, and add an *If-Modified-Since* header to any
+subsequent inbound requests for the same URL.
+
+Think of it as page caching using HTTP semantics.
+
+NOTE: The *Rack::Cache* middleware is always outside of the *Rack::Lock*
+mutex, even in single-threaded apps.
+
+#### Using Rack::Sendfile
+
+When you use the *send\_file* method in a Rails controller, it sets the
+*X-Sendfile* header. *Rack::Sendfile* is responsible for actually
+sending the file.
+
+If your front-end server supports accelerated file sending,
+*Rack::Sendfile* will offload the actual file sending work to the
+front-end server.
+
+You can configure the name of the header that your front-end server uses
+for this purposes using *config.action\_dispatch.x\_sendfile\_header* in
+the appropriate environment config file.
+
+You can learn more about how to use *Rack::Sendfile* with popular
+front-ends in [the Rack::Sendfile
+documentation](http://rubydoc.info/github/rack/rack/master/Rack/Sendfile)
+
+The values for popular servers once they are configured to support
+accelerated file sending:
+
+<ruby>
+
+1. Apache and lighttpd\
+ config.action\_dispatch.x\_sendfile\_header = “X-Sendfile”
+
+<!-- -->
+
+1. nginx\
+ config.action\_dispatch.x\_sendfile\_header = “X-Accel-Redirect”\
+ </ruby>
+
+Make sure to configure your server to support these options following
+the instructions in the *Rack::Sendfile* documentation.
+
+NOTE: The *Rack::Sendfile* middleware is always outside of the
+*Rack::Lock* mutex, even in single-threaded apps.
+
+#### Using ActionDispatch::ParamsParser
+
+*ActionDispatch::ParamsParser* will take parameters from the client in
+JSON and make them available in your controller as *params*.
+
+To use this, your client will need to make a request with JSON-encoded
+parameters and specify the *Content-Type* as *application/json*.
+
+Here’s an example in jQuery:
+
+<plain>\
+jQuery.ajax({\
+ type: ‘POST’,\
+ url: ‘/people’\
+ dataType: ‘json’,\
+ contentType: ‘application/json’,\
+ data: JSON.stringify({ person: { firstName: “Yehuda”, lastName: “Katz”
+} }),
+
+success: function(json) { }\
+});\
+</plain>
+
+*ActionDispatch::ParamsParser* will see the *Content-Type* and your
+params will be *{ :person =\> { :firstName =\> “Yehuda”, :lastName =\>
+“Katz” } }*.
+
+#### Other Middlewares
+
+Rails ships with a number of other middlewares that you might want to
+use in an API app, especially if one of your API clients is the browser:
+
+- *Rack::MethodOverride*: Allows the use of the *\_method* hack to
+ route POST requests to other verbs.
+- *ActionDispatch::Cookies*: Supports the *cookie* method in
+ *ActionController*, including support for signed and encrypted
+ cookies.
+- *ActionDispatch::Flash*: Supports the *flash* mechanism in
+ *ActionController*.
+- *ActionDispatch::BestStandards*: Tells Internet Explorer to use the
+ most standards-compliant available renderer. In production mode, if
+ ChromeFrame is available, use ChromeFrame.
+- Session Management: If a *config.session\_store* is supplied, this
+ middleware makes the session available as the *session* method in
+ *ActionController*.
+
+Any of these middlewares can be adding via:
+
+<ruby>\
+config.middleware.use Rack::MethodOverride\
+</ruby>
+
+#### Removing Middlewares
+
+If you don’t want to use a middleware that is included by default in the
+API-only middleware set, you can remove it using
+*config.middleware.delete*:
+
+<ruby>\
+config.middleware.delete ::Rack::Sendfile\
+</ruby>
+
+Keep in mind that removing these features may remove support for certain
+features in *ActionController*.
+
+### Choosing Controller Modules
+
+An API application (using *ActionController::API*) comes with the
+following controller modules by default:
+
+- *ActionController::UrlFor*: Makes *url\_for* and friends available
+- *ActionController::Redirecting*: Support for *redirect\_to*
+- *ActionController::Rendering*: Basic support for rendering
+- *ActionController::Renderers::All*: Support for *render :json* and
+ friends
+- *ActionController::ConditionalGet*: Support for *stale?*
+- *ActionController::ForceSSL*: Support for *force\_ssl*
+- *ActionController::RackDelegation*: Support for the *request* and
+ *response* methods returning *ActionDispatch::Request* and
+ *ActionDispatch::Response* objects.
+- *ActionController::DataStreaming*: Support for *send\_file* and
+ *send\_data*
+- *AbstractController::Callbacks*: Support for *before\_filter* and
+ friends
+- *ActionController::Instrumentation*: Support for the instrumentation
+ hooks defined by *ActionController* (see [the
+ source](https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/instrumentation.rb)
+ for more).
+- *ActionController::Rescue*: Support for *rescue\_from*.
+
+Other plugins may add additional modules. You can get a list of all
+modules included into *ActionController::API* in the rails console:
+
+<shell>\
+\$ irb\
+\>\> ActionController::API.ancestors -
+ActionController::Metal.ancestors\
+</shell>
+
+#### Adding Other Modules
+
+All ActionController modules know about their dependent modules, so you
+can feel free to include any modules into your controllers, and all
+dependencies will be included and set up as well.
+
+Some common modules you might want to add:
+
+- *AbstractController::Translation*: Support for the *l* and *t*
+ localization and translation methods. These delegate to
+ *I18n.translate* and *I18n.localize*.
+- *ActionController::HTTPAuthentication::Basic* (or *Digest*
+ or +Token): Support for basic, digest or token HTTP authentication.
+- *AbstractController::Layouts*: Support for layouts when rendering.
+- *ActionController::MimeResponds*: Support for content negotiation
+ (*respond\_to*, *respond\_with*).
+- *ActionController::Cookies*: Support for *cookies*, which includes
+ support for signed and encrypted cookies. This requires the cookie
+ middleware.
+
+The best place to add a module is in your *ApplicationController*. You
+can also add modules to individual controllers.
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 412cfd198a..05dd0d2a04 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -435,7 +435,7 @@ end
The simplest rule of thumb is that you should set up a `has_many :through` relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a `has_and_belongs_to_many` relationship (though you'll need to remember to create the joining table in the database).
-You should use `has_many :through` if you need validations, callbacks, or extra attributes on the join model.
+You should use `has_many :through` if you need validations, callbacks or extra attributes on the join model.
### Polymorphic Associations
@@ -590,7 +590,7 @@ If you create an association some time after you build the underlying model, you
If you create a `has_and_belongs_to_many` association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the `:join_table` option, Active Record creates the name by using the lexical order of the class names. So a join between customer and order models will give the default join table name of "customers_orders" because "c" outranks "o" in lexical ordering.
-WARNING: The precedence between model names is calculated using the `<` operator for `String`. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers" to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes", but it in fact generates a join table name of "paper_boxes_papers" (because the underscore '_' is lexicographically _less_ than 's' in common encodings).
+WARNING: The precedence between model names is calculated using the `<=>` operator for `String`. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers" to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes", but it in fact generates a join table name of "paper_boxes_papers" (because the underscore '\_' is lexicographically _less_ than 's' in common encodings).
Whatever the name, you must manually generate the join table with an appropriate migration. For example, consider these associations:
@@ -620,7 +620,7 @@ class CreateAssembliesPartsJoinTable < ActiveRecord::Migration
end
```
-We pass `id: false` to `create_table` because that table does not represent a model. That's required for the association to work properly. If you observe any strange behavior in a `has_and_belongs_to_many` association like mangled models IDs, or exceptions about conflicting IDs, chances are you forgot that bit.
+We pass `id: false` to `create_table` because that table does not represent a model. That's required for the association to work properly. If you observe any strange behavior in a `has_and_belongs_to_many` association like mangled model IDs, or exceptions about conflicting IDs, chances are you forgot that bit.
### Controlling Association Scope
@@ -793,7 +793,7 @@ If the associated object has already been retrieved from the database for this o
##### `association=(associate)`
-The `association=` method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associate object and setting this object's foreign key to the same value.
+The `association=` method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associated object and setting this object's foreign key to the same value.
```ruby
@order.customer = @customer
@@ -1138,7 +1138,7 @@ If the associated object has already been retrieved from the database for this o
##### `association=(associate)`
-The `association=` method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from this object and setting the associate object's foreign key to the same value.
+The `association=` method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from this object and setting the associated object's foreign key to the same value.
```ruby
@supplier.account = @account
@@ -1219,8 +1219,8 @@ Controls what happens to the associated object when its owner is destroyed:
It's necessary not to set or leave `:nullify` option for those associations
that have `NOT NULL` database constraints. If you don't set `dependent` to
destroy such associations you won't be able to change the associated object
-because initial associated object foreign key will be set to unallowed `NULL`
-value.
+because the initial associated object's foreign key will be set to the
+unallowed `NULL` value.
##### `:foreign_key`
@@ -1467,7 +1467,14 @@ The `collection_singular_ids=` method makes the collection contain only the obje
##### `collection.clear`
-The `collection.clear` method removes every object from the collection. This destroys the associated objects if they are associated with `dependent: :destroy`, deletes them directly from the database if `dependent: :delete_all`, and otherwise sets their foreign keys to `NULL`.
+The `collection.clear` method removes all objects from the collection according to the strategy specified by the `dependent` option. If no option is given, it follows the default strategy. The default strategy for `has_many :through` associations is `delete_all`, and for `has_many` associations is to set the foreign keys to `NULL`.
+
+```ruby
+@customer.orders.clear
+```
+
+WARNING: Objects will be deleted if they're associated with `dependent: :destroy`,
+just like `dependent: :delete_all`.
##### `collection.empty?`
@@ -1506,7 +1513,9 @@ The `collection.where` method finds objects within the collection based on the c
##### `collection.exists?(...)`
-The `collection.exists?` method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as `ActiveRecord::Base.exists?`.
+The `collection.exists?` method checks whether an object meeting the supplied
+conditions exists in the collection. It uses the same syntax and options as
+[`ActiveRecord::Base.exists?`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F).
##### `collection.build(attributes = {}, ...)`
@@ -1617,9 +1626,10 @@ end
By convention, Rails assumes that the column used to hold the primary key of the association is `id`. You can override this and explicitly specify the primary key with the `:primary_key` option.
-Let's say that `users` table has `id` as the primary_key but it also has
-`guid` column. And the requirement is that `todos` table should hold
-`guid` column value and not `id` value. This can be achieved like this
+Let's say the `users` table has `id` as the primary_key but it also
+has a `guid` column. The requirement is that the `todos` table should
+hold the `guid` column value as the foreign key and not `id`
+value. This can be achieved like this:
```ruby
class User < ActiveRecord::Base
@@ -1627,8 +1637,8 @@ class User < ActiveRecord::Base
end
```
-Now if we execute `@user.todos.create` then `@todo` record will have
-`user_id` value as the `guid` value of `@user`.
+Now if we execute `@todo = @user.todos.create` then the `@todo`
+record's `user_id` value will be the `guid` value of `@user`.
##### `:source`
@@ -2004,7 +2014,9 @@ The `collection.where` method finds objects within the collection based on the c
##### `collection.exists?(...)`
-The `collection.exists?` method checks whether an object meeting the supplied conditions exists in the collection. It uses the same syntax and options as `ActiveRecord::Base.exists?`.
+The `collection.exists?` method checks whether an object meeting the supplied
+conditions exists in the collection. It uses the same syntax and options as
+[`ActiveRecord::Base.exists?`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-exists-3F).
##### `collection.build(attributes = {})`
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 315d8c14b6..0f5a9e4e39 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -64,10 +64,10 @@ $ cd commandsapp
$ bin/rails server
=> Booting WEBrick
=> Rails 5.0.0 application starting in development on http://localhost:3000
-=> Call with -d to detach
+=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2013-08-07 02:00:01] INFO WEBrick 1.3.1
-[2013-08-07 02:00:01] INFO ruby 2.0.0 (2013-06-27) [x86_64-darwin11.2.0]
+[2013-08-07 02:00:01] INFO ruby 2.2.2 (2015-06-27) [x86_64-darwin11.2.0]
[2013-08-07 02:00:01] INFO WEBrick::HTTPServer#start: pid=69680 port=3000
```
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 4da369be5e..bb6c395c96 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -88,8 +88,6 @@ application. Accepts a valid week day symbol (e.g. `:monday`).
end
```
-* `config.dependency_loading` is a flag that allows you to disable constant autoloading setting it to false. It only has effect if `config.cache_classes` is true, which it is by default in production mode.
-
* `config.eager_load` when true, eager loads all registered `config.eager_load_namespaces`. This includes your application, engines, Rails frameworks and any other registered namespace.
* `config.eager_load_namespaces` registers namespaces that are eager loaded when `config.eager_load` is true. All namespaces in the list must respond to the `eager_load!` method.
@@ -163,8 +161,6 @@ pipeline is enabled. It is set to true by default.
* `config.assets.cache_store` defines the cache store that Sprockets will use. The default is the Rails file store.
-* `config.assets.version` is an option string that is used in MD5 hash generation. This can be changed to force all files to be recompiled.
-
* `config.assets.compile` is a boolean that can be used to turn on live Sprockets compilation in production.
* `config.assets.logger` accepts a logger conforming to the interface of Log4r or the default Ruby `Logger` class. Defaults to the same configured at `config.logger`. Setting `config.assets.logger` to false will turn off served assets logging.
@@ -187,13 +183,15 @@ The full set of methods that can be used in this block are as follows:
* `helper` defines whether or not to generate helpers. Defaults to `true`.
* `integration_tool` defines which integration tool to use. Defaults to `nil`.
* `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`.
+* `javascript_engine` configures the engine to be used (for eg. coffee) when generating assets. Defaults to `:js`.
* `orm` defines which orm to use. Defaults to `false` and will use Active Record by default.
* `resource_controller` defines which generator to use for generating a controller when using `rails generate resource`. Defaults to `:controller`.
+* `resource_route` defines whether a resource route definition should be generated
+ or not. Defaults to `true`.
* `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`.
* `stylesheet_engine` configures the stylesheet engine (for eg. sass) to be used when generating assets. Defaults to `:css`.
-* `test_framework` defines which test framework to use. Defaults to `false` and will use Test::Unit by default.
+* `test_framework` defines which test framework to use. Defaults to `false` and will use Minitest by default.
* `template_engine` defines which template engine to use, such as ERB or Haml. Defaults to `:erb`.
### Configuring Middleware
@@ -201,7 +199,7 @@ The full set of methods that can be used in this block are as follows:
Every Rails application comes with a standard set of middleware which it uses in this order in the development environment:
* `ActionDispatch::SSL` forces every request to be under HTTPS protocol. Will be available if `config.force_ssl` is set to `true`. Options passed to this can be configured by using `config.ssl_options`.
-* `ActionDispatch::Static` is used to serve static assets. Disabled if `config.serve_static_files` is `false`.
+* `ActionDispatch::Static` is used to serve static assets. Disabled if `config.serve_static_files` is `false`. Set `config.static_index` if you need to serve a static directory index file that is not named `index`. For example, to serve `main.html` instead of `index.html` for directory requests, set `config.static_index` to `"main"`.
* `Rack::Lock` wraps the app in mutex so it can only be called by a single thread at a time. Only enabled when `config.cache_classes` is `false`.
* `ActiveSupport::Cache::Strategy::LocalCache` serves as a basic memory backed cache. This cache is not thread safe and is intended only for serving as a temporary memory cache for a single thread.
* `Rack::Runtime` sets an `X-Runtime` header, containing the time (in seconds) taken to execute the request.
@@ -518,6 +516,9 @@ There are a number of settings available on `config.action_mailer`:
config.action_mailer.show_previews = false
```
+* `config.action_mailer.deliver_later_queue_name` specifies the queue name for
+ mailers. By default this is `mailers`.
+
### Configuring Active Support
There are a few configuration options available in Active Support:
@@ -526,7 +527,7 @@ There are a few configuration options available in Active Support:
* `config.active_support.test_order` sets the order that test cases are executed. Possible values are `:random` and `:sorted`. This option is set to `:random` in `config/environments/test.rb` in newly-generated applications. If you have an application that does not specify a `test_order`, it will default to `:sorted`, *until* Rails 5.0, when the default will become `:random`.
-* `config.active_support.escape_html_entities_in_json` enables or disables the escaping of HTML entities in JSON serialization. Defaults to `false`.
+* `config.active_support.escape_html_entities_in_json` enables or disables the escaping of HTML entities in JSON serialization. Defaults to `true`.
* `config.active_support.use_standard_json_time_format` enables or disables serializing dates to ISO 8601 format. Defaults to `true`.
@@ -1126,7 +1127,7 @@ Search Engines Indexing
Sometimes, you may want to prevent some pages of your application to be visible
on search sites like Google, Bing, Yahoo or Duck Duck Go. The robots that index
-these sites will first analyse the `http://your-site.com/robots.txt` file to
+these sites will first analyze the `http://your-site.com/robots.txt` file to
know which pages it is allowed to index.
Rails creates this file for you inside the `/public` folder. By default, it allows
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index 244add0aee..3279c99c42 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -66,7 +66,7 @@ the core team will have to make a judgement call. That said, the distinction
generally just affects which release your patch will get in to; we love feature
submissions! They just won't get backported to maintenance branches.
-If you'd like feedback on an idea for a feature before doing the work for make
+If you'd like feedback on an idea for a feature before doing the work to make
a patch, please send an email to the [rails-core mailing
list](https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core). You
might get no response, which means that everyone is indifferent. You might find
@@ -79,17 +79,17 @@ discussions new features require.
Helping to Resolve Existing Issues
----------------------------------
-As a next step beyond reporting issues, you can help the core team resolve existing issues. If you check the [Everyone's Issues](https://github.com/rails/rails/issues) list in GitHub Issues, you'll find lots of issues already requiring attention. What can you do for these? Quite a bit, actually:
+As a next step beyond reporting issues, you can help the core team resolve existing issues. If you check the [issues list](https://github.com/rails/rails/issues) in GitHub Issues, you'll find lots of issues already requiring attention. What can you do for these? Quite a bit, actually:
### Verifying Bug Reports
For starters, it helps just to verify bug reports. Can you reproduce the reported issue on your own computer? If so, you can add a comment to the issue saying that you're seeing the same thing.
-If something is very vague, can you help squash it down into something specific? Maybe you can provide additional information to help reproduce a bug, or help by eliminating needless steps that aren't required to demonstrate the problem.
+If an issue is very vague, can you help narrow it down to something more specific? Maybe you can provide additional information to help reproduce a bug, or help by eliminating needless steps that aren't required to demonstrate the problem.
If you find a bug report without a test, it's very useful to contribute a failing test. This is also a great way to get started exploring the source code: looking at the existing test files will teach you how to write more tests. New tests are best contributed in the form of a patch, as explained later on in the "Contributing to the Rails Code" section.
-Anything you can do to make bug reports more succinct or easier to reproduce is a help to folks trying to write code to fix those bugs - whether you end up writing the code yourself or not.
+Anything you can do to make bug reports more succinct or easier to reproduce helps folks trying to write code to fix those bugs - whether you end up writing the code yourself or not.
### Testing Patches
@@ -117,7 +117,7 @@ Once you're happy that the pull request contains a good change, comment on the G
>I like the way you've restructured that code in generate_finder_sql - much nicer. The tests look good too.
-If your comment simply says "+1", then odds are that other reviewers aren't going to take it too seriously. Show that you took the time to review the pull request.
+If your comment simply reads "+1", then odds are that other reviewers aren't going to take it too seriously. Show that you took the time to review the pull request.
Contributing to the Rails Documentation
---------------------------------------
@@ -579,8 +579,7 @@ following:
```bash
$ git fetch upstream
$ git checkout my_pull_request
-$ git rebase upstream/master
-$ git rebase -i
+$ git rebase -i upstream/master
< Choose 'squash' for all of your commits except the first one. >
< Edit the commit message to make sense, and describe all your changes. >
diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md
index ec3ac62b8c..dc1df8f229 100644
--- a/guides/source/debugging_rails_applications.md
+++ b/guides/source/debugging_rails_applications.md
@@ -317,7 +317,7 @@ For example:
=> Notice: server is listening on all interfaces (0.0.0.0). Consider using 127.0.0.1 (--binding option)
=> Ctrl-C to shutdown server
[2014-04-11 13:11:47] INFO WEBrick 1.3.1
-[2014-04-11 13:11:47] INFO ruby 2.1.1 (2014-02-24) [i686-linux]
+[2014-04-11 13:11:47] INFO ruby 2.2.2 (2015-04-13) [i686-linux]
[2014-04-11 13:11:47] INFO WEBrick::HTTPServer#start: pid=6370 port=3000
@@ -531,8 +531,11 @@ command later in this guide).
And then ask again for the instance_variables:
```
-(byebug) instance_variables.include? "@articles"
-true
+(byebug) instance_variables
+[:@_action_has_layout, :@_routes, :@_headers, :@_status, :@_request,
+ :@_response, :@_env, :@_prefixes, :@_lookup_context, :@_action_name,
+ :@_response_body, :@marked_for_same_origin_verification, :@_config,
+ :@articles]
```
Now `@articles` is included in the instance variables, because the line defining it
@@ -778,7 +781,7 @@ will be stopped and you will have to start it again.
### Settings
-`byebug` has a few available options to tweak its behaviour:
+`byebug` has a few available options to tweak its behavior:
* `set autoreload`: Reload source code when changed (defaults: true).
* `set autolist`: Execute `list` command on every breakpoint (defaults: true).
diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md
index 989b29956c..295e48f493 100644
--- a/guides/source/development_dependencies_install.md
+++ b/guides/source/development_dependencies_install.md
@@ -21,14 +21,14 @@ The easiest and recommended way to get a development environment ready to hack i
The Hard Way
------------
-In case you can't use the Rails development box, see section above, these are the steps to manually build a development box for Ruby on Rails core development.
+In case you can't use the Rails development box, see section below, these are the steps to manually build a development box for Ruby on Rails core development.
### Install Git
Ruby on Rails uses Git for source code control. The [Git homepage](http://git-scm.com/) has installation instructions. There are a variety of resources on the net that will help you get familiar with Git:
* [Try Git course](http://try.github.io/) is an interactive course that will teach you the basics.
-* The [official Documentation](http://git-scm.com/documentation) is pretty comprehensive and also contains some videos with the basics of Git
+* The [official Documentation](http://git-scm.com/documentation) is pretty comprehensive and also contains some videos with the basics of Git.
* [Everyday Git](http://schacon.github.io/git/everyday.html) will teach you just enough about Git to get by.
* The [PeepCode screencast](https://peepcode.com/products/git) on Git is easier to follow.
* [GitHub](http://help.github.com) offers links to a variety of Git resources.
diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml
index 7ae3640937..9145aee009 100644
--- a/guides/source/documents.yaml
+++ b/guides/source/documents.yaml
@@ -11,7 +11,7 @@
-
name: Active Record Basics
url: active_record_basics.html
- description: This guide will get you started with models, persistence to database and the Active Record pattern and library.
+ description: This guide will get you started with models, persistence to database, and the Active Record pattern and library.
-
name: Active Record Migrations
url: active_record_migrations.html
@@ -19,7 +19,7 @@
-
name: Active Record Validations
url: active_record_validations.html
- description: This guide covers how you can use Active Record validations
+ description: This guide covers how you can use Active Record validations.
-
name: Active Record Callbacks
url: active_record_callbacks.html
@@ -74,7 +74,7 @@
-
name: Rails Internationalization API
url: i18n.html
- description: This guide covers how to add internationalization to your applications. Your application will be able to translate content to different languages, change pluralization rules, use correct date formats for each country and so on.
+ description: This guide covers how to add internationalization to your applications. Your application will be able to translate content to different languages, change pluralization rules, use correct date formats for each country, and so on.
-
name: Action Mailer Basics
url: action_mailer_basics.html
@@ -82,7 +82,7 @@
-
name: Active Job Basics
url: active_job_basics.html
- description: This guide provides you with all you need to get started in creating, enqueueing and executing background jobs.
+ description: This guide provides you with all you need to get started creating, enqueuing, and executing background jobs.
-
name: Testing Rails Applications
work_in_progress: true
@@ -116,7 +116,7 @@
name: The Rails Initialization Process
work_in_progress: true
url: initialization.html
- description: This guide explains the internals of the Rails initialization process as of Rails 4
+ description: This guide explains the internals of the Rails initialization process as of Rails 4.
-
name: Autoloading and Reloading Constants
url: autoloading_and_reloading_constants.html
diff --git a/guides/source/engines.md b/guides/source/engines.md
index bcb0ee7d5d..3b1588b75a 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -582,7 +582,7 @@ the comments, however, is not quite right yet. If you were to create a comment
right now, you would see this error:
```
-Missing partial blorgh/comments/comment with {:handlers=>[:erb, :builder],
+Missing partial blorgh/comments/_comment with {:handlers=>[:erb, :builder],
:formats=>[:html], :locale=>[:en, :en]}. Searched in: *
"/Users/ryan/Sites/side_projects/blorgh/test/dummy/app/views" *
"/Users/ryan/Sites/side_projects/blorgh/app/views"
@@ -591,7 +591,7 @@ Missing partial blorgh/comments/comment with {:handlers=>[:erb, :builder],
The engine is unable to find the partial required for rendering the comments.
Rails looks first in the application's (`test/dummy`) `app/views` directory and
then in the engine's `app/views` directory. When it can't find it, it will throw
-this error. The engine knows to look for `blorgh/comments/comment` because the
+this error. The engine knows to look for `blorgh/comments/_comment` because the
model object it is receiving is from the `Blorgh::Comment` class.
This partial will be responsible for rendering just the comment text, for now.
@@ -689,8 +689,8 @@ haven't been copied over already. The first run for this command will output
something such as this:
```bash
-Copied migration [timestamp_1]_create_blorgh_articles.rb from blorgh
-Copied migration [timestamp_2]_create_blorgh_comments.rb from blorgh
+Copied migration [timestamp_1]_create_blorgh_articles.blorgh.rb from blorgh
+Copied migration [timestamp_2]_create_blorgh_comments.blorgh.rb from blorgh
```
The first timestamp (`[timestamp_1]`) will be the current time, and the second
@@ -822,9 +822,9 @@ Notice that only _one_ migration was copied over here. This is because the first
two migrations were copied over the first time this command was run.
```
-NOTE Migration [timestamp]_create_blorgh_articles.rb from blorgh has been skipped. Migration with the same name already exists.
-NOTE Migration [timestamp]_create_blorgh_comments.rb from blorgh has been skipped. Migration with the same name already exists.
-Copied migration [timestamp]_add_author_id_to_blorgh_articles.rb from blorgh
+NOTE Migration [timestamp]_create_blorgh_articles.blorgh.rb from blorgh has been skipped. Migration with the same name already exists.
+NOTE Migration [timestamp]_create_blorgh_comments.blorgh.rb from blorgh has been skipped. Migration with the same name already exists.
+Copied migration [timestamp]_add_author_id_to_blorgh_articles.blorgh.rb from blorgh
```
Run the migration using:
diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md
index 853227e2a1..8f7d97844e 100644
--- a/guides/source/form_helpers.md
+++ b/guides/source/form_helpers.md
@@ -291,8 +291,8 @@ You can create a similar binding without actually creating `<form>` tags with th
```erb
<%= form_for @person, url: {action: "create"} do |person_form| %>
<%= person_form.text_field :name %>
- <%= fields_for @person.contact_detail do |contact_details_form| %>
- <%= contact_details_form.text_field :phone_number %>
+ <%= fields_for @person.contact_detail do |contact_detail_form| %>
+ <%= contact_detail_form.text_field :phone_number %>
<% end %>
<% end %>
```
@@ -441,7 +441,7 @@ Whenever Rails sees that the internal value of an option being generated matches
TIP: The second argument to `options_for_select` must be exactly equal to the desired internal value. In particular if the value is the integer `2` you cannot pass `"2"` to `options_for_select` - you must pass `2`. Be aware of values extracted from the `params` hash as they are all strings.
-WARNING: when `:include_blank` or `:prompt` are not present, `:include_blank` is forced true if the select attribute `required` is true, display `size` is one and `multiple` is not true.
+WARNING: When `:include_blank` or `:prompt` are not present, `:include_blank` is forced true if the select attribute `required` is true, display `size` is one and `multiple` is not true.
You can add arbitrary attributes to the options using hashes:
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index 68ab454660..e64a788ac2 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -400,7 +400,7 @@ a controller called `ArticlesController`. You can do this by running this
command:
```bash
-$ bin/rails g controller articles
+$ bin/rails generate controller articles
```
If you open up the newly generated `app/controllers/articles_controller.rb`
@@ -1240,7 +1240,9 @@ article we want to show the form back to the user.
We reuse the `article_params` method that we defined earlier for the create
action.
-TIP: It is not necessary to pass all the attributes to `update`. For example, if `@article.update(title: 'A new title')` were called, Rails would only update the `title` attribute, leaving all other attributes untouched.
+TIP: It is not necessary to pass all the attributes to `update`. For example,
+if `@article.update(title: 'A new title')` was called, Rails would only update
+the `title` attribute, leaving all other attributes untouched.
Finally, we want to show a link to the `edit` action in the list of all the
articles, so let's add that now to `app/views/articles/index.html.erb` to make
@@ -1488,7 +1490,7 @@ appear.
![Confirm Dialog](images/getting_started/confirm_dialog.png)
TIP: Learn more about jQuery Unobtrusive Adapter (jQuery UJS) on
-[Working With Javascript in Rails](working_with_javascript_in_rails.html) guide.
+[Working With JavaScript in Rails](working_with_javascript_in_rails.html) guide.
Congratulations, you can now create, show, list, update and destroy
articles.
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index 51eaf4ba5a..9f0ed1a85b 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -500,7 +500,10 @@ You can make use of this feature, e.g. when working with a large amount of stati
### Organization of Locale Files
-When you are using the default SimpleStore shipped with the i18n library, dictionaries are stored in plain-text files on the disc. Putting translations for all parts of your application in one file per locale could be hard to manage. You can store these files in a hierarchy which makes sense to you.
+When you are using the default SimpleStore shipped with the i18n library,
+dictionaries are stored in plain-text files on the disk. Putting translations
+for all parts of your application in one file per locale could be hard to
+manage. You can store these files in a hierarchy which makes sense to you.
For example, your `config/locales` directory could look like this:
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index 199545a3b3..43083ebb86 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -53,11 +53,11 @@ require "rails/cli"
```
The file `railties/lib/rails/cli` in turn calls
-`Rails::AppRailsLoader.exec_app_rails`.
+`Rails::AppLoader.exec_app`.
-### `railties/lib/rails/app_rails_loader.rb`
+### `railties/lib/rails/app_loader.rb`
-The primary goal of the function `exec_app_rails` is to execute your app's
+The primary goal of the function `exec_app` is to execute your app's
`bin/rails`. If the current directory does not have a `bin/rails`, it will
navigate upwards until it finds a `bin/rails` executable. Thus one can invoke a
`rails` command from anywhere inside a rails application.
@@ -106,6 +106,7 @@ A standard Rails application depends on several gems, specifically:
* activemodel
* activerecord
* activesupport
+* activejob
* arel
* builder
* bundler
@@ -532,6 +533,7 @@ require "rails"
action_controller
action_view
action_mailer
+ active_job
rails/test_unit
sprockets
).each do |framework|
@@ -555,9 +557,8 @@ I18n and Rails configuration are all being defined here.
The rest of `config/application.rb` defines the configuration for the
`Rails::Application` which will be used once the application is fully
initialized. When `config/application.rb` has finished loading Rails and defined
-the application namespace, we go back to `config/environment.rb`,
-where the application is initialized. For example, if the application was called
-`Blog`, here we would find `Rails.application.initialize!`, which is
+the application namespace, we go back to `config/environment.rb`. Here, the
+application is initialized with `Rails.application.initialize!`, which is
defined in `rails/application.rb`.
### `railties/lib/rails/application.rb`
diff --git a/guides/source/kindle/welcome.html.erb b/guides/source/kindle/welcome.html.erb
index 610a71570f..ef3397f58f 100644
--- a/guides/source/kindle/welcome.html.erb
+++ b/guides/source/kindle/welcome.html.erb
@@ -2,4 +2,6 @@
<h3>Kindle Edition</h3>
-The Kindle Edition of the Rails Guides should be considered a work in progress. Feedback is really welcome. Please see the "Feedback" section at the end of each guide for instructions.
+<div>
+ The Kindle Edition of the Rails Guides should be considered a work in progress. Feedback is really welcome. Please see the "Feedback" section at the end of each guide for instructions.
+</div>
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index 737f392995..94cd7297e2 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -103,32 +103,6 @@ In most cases, the `ActionController::Base#render` method does the heavy lifting
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.
-#### Rendering Nothing
-
-Perhaps the simplest thing you can do with `render` is to render nothing at all:
-
-```ruby
-render nothing: true
-```
-
-If you look at the response for this using cURL, you will see the following:
-
-```bash
-$ curl -i 127.0.0.1:3000/books
-HTTP/1.1 200 OK
-Connection: close
-Date: Sun, 24 Jan 2010 09:25:18 GMT
-Transfer-Encoding: chunked
-Content-Type: */*; charset=utf-8
-X-Runtime: 0.014297
-Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
-Cache-Control: no-cache
-```
-
-We see there is an empty response (no data after the `Cache-Control` line), but the request was successful because Rails has set the response to 200 OK. You can set the `:status` option on render to change this response. Rendering nothing can be useful for Ajax requests where all you want to send back to the browser is an acknowledgment that the request was completed.
-
-TIP: You should probably be using the `head` method, discussed later in this guide, instead of `render :nothing`. This provides additional flexibility and makes it explicit that you're only generating HTTP headers.
-
#### Rendering an Action's View
If you want to render the view that corresponds to a different template within the same controller, you can use `render` with the name of the view:
diff --git a/guides/source/nested_model_forms.md b/guides/source/nested_model_forms.md
index 1937369776..121cf2b185 100644
--- a/guides/source/nested_model_forms.md
+++ b/guides/source/nested_model_forms.md
@@ -106,7 +106,7 @@ Consider the following typical RESTful controller which will prepare a new Perso
class PeopleController < ApplicationController
def new
@person = Person.new
- @person.built_address
+ @person.build_address
2.times { @person.projects.build }
end
diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md
index b3e1874048..b7364536c3 100644
--- a/guides/source/rails_application_templates.md
+++ b/guides/source/rails_application_templates.md
@@ -215,7 +215,7 @@ CODE
### yes?(question) or no?(question)
-These methods let you ask questions from templates and decide the flow based on the user's answer. Let's say you want to freeze rails only if the user wants to:
+These methods let you ask questions from templates and decide the flow based on the user's answer. Let's say you want to Freeze Rails only if the user wants to:
```ruby
rake("rails:freeze:gems") if yes?("Freeze rails gems?")
diff --git a/guides/source/routing.md b/guides/source/routing.md
index b1e4c8ad86..52f11f92bd 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -809,13 +809,16 @@ As long as `Sprockets` responds to `call` and returns a `[status, headers, body]
NOTE: For the curious, `'articles#index'` actually expands out to `ArticlesController.action(:index)`, which returns a valid Rack application.
-If you specify a rack application as the endpoint for a matcher remember that the route will be unchanged in the receiving application. With the following route your rack application should expect the route to be '/admin':
+If you specify a Rack application as the endpoint for a matcher, remember that
+the route will be unchanged in the receiving application. With the following
+route your Rack application should expect the route to be '/admin':
```ruby
match '/admin', to: AdminApp, via: :all
```
-If you would prefer to have your rack application receive requests at the root path instead use mount:
+If you would prefer to have your Rack application receive requests at the root
+path instead, use mount:
```ruby
mount AdminApp, at: '/admin'
diff --git a/guides/source/security.md b/guides/source/security.md
index 390375b75d..93580d4d4e 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -572,7 +572,7 @@ NOTE: _When sanitizing, protecting or verifying something, prefer whitelists ove
A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although sometimes it is not possible to create a whitelist (in a SPAM filter, for example), _prefer to use whitelist approaches_:
-* Use before_action only: [...] instead of except: [...]. This way you don't forget to turn it off for newly added actions.
+* Use before_action except: [...] instead of only: [...] for security-related actions. This way you don't forget to enable security checks for newly added actions.
* Allow &lt;strong&gt; instead of removing &lt;script&gt; against Cross-Site Scripting (XSS). See below for details.
* Don't try to correct user input by blacklists:
* This will make the attack work: "&lt;sc&lt;script&gt;ript&gt;".gsub("&lt;script&gt;", "")
@@ -712,7 +712,7 @@ The log files on www.attacker.com will read like this:
GET http://www.attacker.com/_app_session=836c1c25278e5b321d6bea4f19cb57e2
```
-You can mitigate these attacks (in the obvious way) by adding the **httpOnly** flag to cookies, so that document.cookie may not be read by JavaScript. Http only cookies can be used from IE v6.SP1, Firefox v2.0.0.5 and Opera 9.5. Safari is still considering, it ignores the option. But other, older browsers (such as WebTV and IE 5.5 on Mac) can actually cause the page to fail to load. Be warned that cookies [will still be visible using Ajax](http://ha.ckers.org/blog/20070719/firefox-implements-httponly-and-is-vulnerable-to-xmlhttprequest/), though.
+You can mitigate these attacks (in the obvious way) by adding the **httpOnly** flag to cookies, so that document.cookie may not be read by JavaScript. Http only cookies can be used from IE v6.SP1, Firefox v2.0.0.5 and Opera 9.5. Safari is still considering, it ignores the option. But other, older browsers (such as WebTV and IE 5.5 on Mac) can actually cause the page to fail to load. Be warned that cookies [will still be visible using Ajax](https://www.owasp.org/index.php/HTTPOnly#Browsers_Supporting_HttpOnly), though.
##### Defacement
@@ -971,7 +971,7 @@ request:
| `{ "person": [null, null, ...] }` | `{ :person => [] }` |
| `{ "person": ["foo", null] }` | `{ :person => ["foo"] }` |
-It is possible to return to old behaviour and disable `deep_munge` configuring
+It is possible to return to old behavior and disable `deep_munge` configuring
your application if you are aware of the risk and know how to handle it:
```ruby
@@ -1033,4 +1033,5 @@ The security landscape shifts and it is important to keep up to date, because mi
* Subscribe to the Rails security [mailing list](http://groups.google.com/group/rubyonrails-security)
* [Keep up to date on the other application layers](http://secunia.com/) (they have a weekly newsletter, too)
-* A [good security blog](http://ha.ckers.org/blog/) including the [Cross-Site scripting Cheat Sheet](http://ha.ckers.org/xss.html)
+* A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet](https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet)
+
diff --git a/guides/source/testing.md b/guides/source/testing.md
index cc469f4dae..230e8162f3 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -377,7 +377,7 @@ Notice the 'E' in the output. It denotes a test with error.
NOTE: The execution of each test method stops as soon as any error or an
assertion failure is encountered, and the test suite continues with the next
method. All test methods are executed in random order. The
-[`config.active_support.test_order` option](http://edgeguides.rubyonrails.org/configuring.html#configuring-active-support)
+[`config.active_support.test_order` option](configuring.html#configuring-active-support)
can be used to configure test order.
When a test fails you are presented with the corresponding backtrace. By default
@@ -435,7 +435,7 @@ specify to make your test failure messages clearer. It's not required.
| `assert_nothing_raised( exception1, exception2, ... ) { block }` | Ensures that the given block doesn't raise one of the given exceptions.|
| `assert_instance_of( class, obj, [msg] )` | Ensures that `obj` is an instance of `class`.|
| `assert_not_instance_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class`.|
-| `assert_kind_of( class, obj, [msg] )` | Ensures that `obj` is or descends from `class`.|
+| `assert_kind_of( class, obj, [msg] )` | Ensures that `obj` is an instance of `class` or is descending from it.|
| `assert_not_kind_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class` and is not descending from it.|
| `assert_respond_to( obj, symbol, [msg] )` | Ensures that `obj` responds to `symbol`.|
| `assert_not_respond_to( obj, symbol, [msg] )` | Ensures that `obj` does not respond to `symbol`.|
@@ -462,12 +462,11 @@ Rails adds some custom assertions of its own to the `minitest` framework:
| Assertion | Purpose |
| --------------------------------------------------------------------------------- | ------- |
| `assert_difference(expressions, difference = 1, message = nil) {...}` | Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.|
-| `assert_no_difference(expressions, message = nil, &amp;block)` | Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.|
+| `assert_no_difference(expressions, message = nil, &block)` | Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.|
| `assert_recognizes(expected_options, path, extras={}, message=nil)` | Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.|
| `assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)` | Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.|
| `assert_response(type, message = nil)` | Asserts that the response comes with a specific status code. You can specify `:success` to indicate 200-299, `:redirect` to indicate 300-399, `:missing` to indicate 404, or `:error` to match the 500-599 range. You can also pass an explicit status number or its symbolic equivalent. For more information, see [full list of status codes](http://rubydoc.info/github/rack/rack/master/Rack/Utils#HTTP_STATUS_CODES-constant) and how their [mapping](http://rubydoc.info/github/rack/rack/master/Rack/Utils#SYMBOL_TO_STATUS_CODE-constant) works.|
| `assert_redirected_to(options = {}, message=nil)` | Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that `assert_redirected_to(controller: "weblog")` will also match the redirection of `redirect_to(controller: "weblog", action: "show")` and so on. You can also pass named routes such as `assert_redirected_to root_path` and Active Record objects such as `assert_redirected_to @article`.|
-| `assert_template(expected = nil, message=nil)` | Asserts that the request was rendered with the appropriate template file.|
You'll see the usage of some of these assertions in the next chapter.
@@ -484,7 +483,7 @@ All the basic assertions such as `assert_equal` defined in `Minitest::Assertions
Each of these classes include `Minitest::Assertions`, allowing us to use all of the basic assertions in our tests.
-NOTE: For more information on `Minitest`, refer to [Minitest](http://ruby-doc.org/stdlib-2.1.0/libdoc/minitest/rdoc/MiniTest.html)
+NOTE: For more information on `Minitest`, refer to [Minitest](http://docs.seattlerb.org/minitest)
Functional Tests for Your Controllers
-------------------------------------
@@ -506,16 +505,18 @@ Now that we have used Rails scaffold generator for our `Article` resource, it ha
Let me take you through one such test, `test_should_get_index` from the file `articles_controller_test.rb`.
```ruby
+# articles_controller_test.rb
class ArticlesControllerTest < ActionController::TestCase
test "should get index" do
get :index
assert_response :success
- assert_not_nil assigns(:articles)
+ assert_includes @response.body, 'Articles'
end
end
```
-In the `test_should_get_index` test, Rails simulates a request on the action called `index`, making sure the request was successful and also ensuring that it assigns a valid `articles` instance variable.
+In the `test_should_get_index` test, Rails simulates a request on the action called `index`, making sure the request was successful
+and also ensuring that the right response body has been generated.
The `get` method kicks off the web request and populates the results into the response. It accepts 4 arguments:
@@ -553,7 +554,7 @@ test "should create article" do
post :create, params: { article: { title: 'Some title' } }
end
- assert_redirected_to article_path(assigns(:article))
+ assert_redirected_to article_path(Article.last)
end
```
@@ -580,11 +581,11 @@ To test AJAX requests, you can specify the `xhr: true` option to `get`, `post`,
`patch`, `put`, and `delete` methods:
```ruby
-test "ajax request responds with no layout" do
+test "ajax request" do
get :show, params: { id: articles(:first).id }, xhr: true
- assert_template :index
- assert_template layout: nil
+ assert_equal 'hello world', @response.body
+ assert_equal "text/javascript", @response.content_type
end
```
@@ -592,20 +593,16 @@ end
After a request has been made and processed, you will have 4 Hash objects ready for use:
-* `assigns` - Any objects that are stored as instance variables in actions for use in views.
* `cookies` - Any cookies that are set.
* `flash` - Any objects living in the flash.
* `session` - Any object living in session variables.
-As is the case with normal Hash objects, you can access the values by referencing the keys by string. You can also reference them by symbol name, except for `assigns`. For example:
+As is the case with normal Hash objects, you can access the values by referencing the keys by string. You can also reference them by symbol name. For example:
```ruby
flash["gordon"] flash[:gordon]
session["shmession"] session[:shmession]
cookies["are_good_for_u"] cookies[:are_good_for_u]
-
-# Because you can't use assigns[:something] for historical reasons:
-assigns["something"] assigns(:something)
```
### Instance Variables Available
@@ -633,46 +630,6 @@ get :index # simulate the request with custom header
post :create # simulate the request with custom env variable
```
-### Testing Templates and Layouts
-
-Eventually, you may want to test whether a specific layout is rendered in the view of a response.
-
-#### Asserting Templates
-
-If you want to make sure that the response rendered the correct template and layout, you can use the `assert_template`
-method:
-
-```ruby
-test "index should render correct template and layout" do
- get :index
- assert_template :index
- assert_template layout: "layouts/application"
-
- # You can also pass a regular expression.
- assert_template layout: /layouts\/application/
-end
-```
-
-NOTE: You cannot test for template and layout at the same time, with a single call to `assert_template`.
-
-WARNING: You must include the "layouts" directory name even if you save your layout file in this standard layout directory. Hence, `assert_template layout: "application"` will not work.
-
-#### Asserting Partials
-
-If your view renders any partial, when asserting for the layout, you can to assert for the partial at the same time.
-Otherwise, assertion will fail.
-
-Remember, we added the "_form" partial to our new Article view? Let's write an assertion for that in the `:new` action now:
-
-```ruby
-test "new should render correct layout" do
- get :new
- assert_template layout: "layouts/application", partial: "_form"
-end
-```
-
-This is the correct way to assert for when the view renders a partial with a given name. As identified by the `:partial` key passed to the `assert_template` call.
-
### Testing `flash` notices
If you remember from earlier one of the Four Hashes of the Apocalypse was `flash`.
@@ -688,7 +645,7 @@ test "should create article" do
post :create, params: { article: { title: 'Some title' } }
end
- assert_redirected_to article_path(assigns(:article))
+ assert_redirected_to article_path(Article.last)
assert_equal 'Article was successfully created.', flash[:notice]
end
```
@@ -781,7 +738,7 @@ We can also add a test for updating an existing Article.
test "should update article" do
article = articles(:one)
patch :update, params: { id: article.id, article: { title: "updated" } }
- assert_redirected_to article_path(assigns(:article))
+ assert_redirected_to article_path(article)
end
```
@@ -820,7 +777,7 @@ class ArticlesControllerTest < ActionController::TestCase
test "should update article" do
patch :update, params: { id: @article.id, article: { title: "updated" } }
- assert_redirected_to article_path(assigns(:article))
+ assert_redirected_to article_path(@article)
end
end
```
@@ -857,7 +814,6 @@ class ProfileControllerTest < ActionController::TestCase
get :show
assert_response :success
- assert_equal users(:david), assigns(:user)
end
end
```
@@ -898,7 +854,7 @@ For more information on routing assertions available in Rails, see the API docum
Testing Views
-------------
-Testing the response to your request by asserting the presence of key HTML elements and their content is a common way to test the views of your application. The `assert_select` method allows you to query HTML elements of the response by using a simple yet powerful syntax.
+Testing the response to your request by asserting the presence of key HTML elements and their content is a common way to test the views of your application. Like route tests, view tests reside in `test/controllers/` or are part of controller tests. The `assert_select` method allows you to query HTML elements of the response by using a simple yet powerful syntax.
There are two forms of `assert_select`:
@@ -1071,14 +1027,12 @@ How about testing our ability to create a new article in our blog and see the re
test "can create an article" do
get "/articles/new"
assert_response :success
- assert_template "articles/new", partial: "articles/_form"
post "/articles",
params: { article: { title: "can create", body: "article successfully." } }
assert_response :redirect
follow_redirect!
assert_response :success
- assert_template "articles/show"
assert_select "p", "Title:\n can create"
end
```
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 49834fa8a2..17309d4b47 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -460,7 +460,7 @@ If your application currently depend on MultiJSON directly, you have a few optio
WARNING: Do not simply replace `MultiJson.dump` and `MultiJson.load` with
`JSON.dump` and `JSON.load`. These JSON gem APIs are meant for serializing and
-deserializing arbitrary Ruby objects and are generally [unsafe](http://www.ruby-doc.org/stdlib-2.0.0/libdoc/json/rdoc/JSON.html#method-i-load).
+deserializing arbitrary Ruby objects and are generally [unsafe](http://www.ruby-doc.org/stdlib-2.2.2/libdoc/json/rdoc/JSON.html#method-i-load).
#### JSON gem compatibility
@@ -926,7 +926,7 @@ Rails 4.0 extracted Active Resource to its own gem. If you still need the featur
Please note that you should wait to set `secret_key_base` until you have 100% of your userbase on Rails 4.x and are reasonably sure you will not need to rollback to Rails 3.x. This is because cookies signed based on the new `secret_key_base` in Rails 4.x are not backwards compatible with Rails 3.x. You are free to leave your existing `secret_token` in place, not set the new `secret_key_base`, and ignore the deprecation warnings until you are reasonably sure that your upgrade is otherwise complete.
-If you are relying on the ability for external applications or Javascript to be able to read your Rails app's signed session cookies (or signed cookies in general) you should not set `secret_key_base` until you have decoupled these concerns.
+If you are relying on the ability for external applications or JavaScript to be able to read your Rails app's signed session cookies (or signed cookies in general) you should not set `secret_key_base` until you have decoupled these concerns.
* Rails 4.0 encrypts the contents of cookie-based sessions if `secret_key_base` has been set. Rails 3.x signed, but did not encrypt, the contents of cookie-based session. Signed cookies are "secure" in that they are verified to have been generated by your app and are tamper-proof. However, the contents can be viewed by end users, and encrypting the contents eliminates this caveat/concern without a significant performance penalty.
@@ -940,6 +940,8 @@ Please read [Pull Request #9978](https://github.com/rails/rails/pull/9978) for d
* Rails 4.0 has removed the XML parameters parser. You will need to add the `actionpack-xml_parser` gem if you require this feature.
+* Rails 4.0 changes the default `layout` lookup set using symbols or procs that return nil. To get the "no layout" behavior, return false instead of nil.
+
* Rails 4.0 changes the default memcached client from `memcache-client` to `dalli`. To upgrade, simply add `gem 'dalli'` to your `Gemfile`.
* Rails 4.0 deprecates the `dom_id` and `dom_class` methods in controllers (they are fine in views). You will need to include the `ActionView::RecordIdentifier` module in controllers requiring this feature.
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index e3856a285a..f3d3a83afc 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -191,34 +191,6 @@ $(document).ready ->
Obviously, you'll want to be a bit more sophisticated than that, but it's a
start. You can see more about the events [in the jquery-ujs wiki](https://github.com/rails/jquery-ujs/wiki/ajax).
-Another possibility is returning javascript directly from the server side on
-remote calls:
-
-```ruby
-# articles_controller
-def create
- respond_to do |format|
- if @article.save
- format.html { ... }
- format.js do
- render js: <<-endjs
- alert('Article saved successfully!');
- window.location = '#{article_path(@article)}';
- endjs
- end
- else
- format.html { ... }
- format.js do
- render js: "alert('There are empty fields in the form!');"
- end
- end
- end
-end
-```
-
-NOTE: If javascript is disabled in the user browser, `format.html { ... }`
-block should be executed as fallback.
-
### form_tag
[`form_tag`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-form_tag)
diff --git a/install.rb b/install.rb
index 96e4153165..933e5ee9a2 100644
--- a/install.rb
+++ b/install.rb
@@ -7,10 +7,10 @@ end
%w( activesupport activemodel activerecord actionpack actionview actionmailer railties activejob ).each do |framework|
puts "Installing #{framework}..."
- `cd #{framework} && gem build #{framework}.gemspec && gem install #{framework}-#{version}.gem --no-ri --no-rdoc && rm #{framework}-#{version}.gem`
+ `cd #{framework} && gem build #{framework}.gemspec && gem install #{framework}-#{version}.gem --no-document && rm #{framework}-#{version}.gem`
end
puts "Installing rails..."
`gem build rails.gemspec`
-`gem install rails-#{version}.gem --no-ri --no-rdoc `
+`gem install rails-#{version}.gem --no-document `
`rm rails-#{version}.gem`
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index b61c4448b5..3536929d00 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,29 @@
+* Add support for API only apps.
+ Middleware stack was slimmed down and it has only the needed
+ middleware for API apps & generators generates the right files,
+ folders and configurations.
+
+ *Santiago Pastorino & Jorge Bejar*
+
+* Make generated scaffold functional tests work inside engines.
+
+ *Yuji Yaginuma*
+
+* Generator a `.keep` file in the `tmp` folder by default as many scripts
+ assume the existence of this folder and most would fail if it is absent.
+
+ See #20299.
+
+ *Yoong Kang Lim*, *Sunny Juneja*
+
+* `config.static_index` configures directory `index.html` filename
+
+ Set `config.static_index` to serve a static directory index file not named
+ `index`. E.g. to serve `main.html` instead of `index.html` for directory
+ requests, set `config.static_index` to `"main"`.
+
+ *Eliot Sykes*
+
* `bin/setup` uses built-in rake tasks (`log:clear`, `tmp:clear`).
*Mohnish Thallavajhula*
diff --git a/railties/Rakefile b/railties/Rakefile
index 9a377ce4ee..4393f45790 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -1,5 +1,4 @@
require 'rake/testtask'
-require 'rubygems/package_task'
task :default => :test
@@ -31,20 +30,3 @@ Rake::TestTask.new('test:regular') do |t|
t.verbose = true
t.ruby_opts = ["--dev"] if defined?(JRUBY_VERSION)
end
-
-# Generate GEM ----------------------------------------------------------------------------
-
-spec = eval(File.read('railties.gemspec'))
-
-Gem::PackageTask.new(spec) do |pkg|
- pkg.gem_spec = spec
-end
-
-# Publishing -------------------------------------------------------
-
-desc "Release to rubygems"
-task :release => :package do
- require 'rake/gemcutter'
- Rake::Gemcutter::Tasks.new(spec).define
- Rake::Task['gem:push'].invoke
-end
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index b1f7c29b5a..fe789f3c2a 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -52,6 +52,11 @@ module Rails
end
end
+ # Returns a Pathname object of the current rails project,
+ # otherwise it returns nil if there is no project:
+ #
+ # Rails.root
+ # # => #<Pathname:/Users/someuser/some/path/project>
def root
application && application.config.root
end
@@ -94,6 +99,11 @@ module Rails
groups
end
+ # Returns a Pathname object of the public folder of the current
+ # rails project, otherwise it returns nil if there is no project:
+ #
+ # Rails.public_path
+ # # => #<Pathname:/Users/someuser/some/path/project/public>
def public_path
application && Pathname.new(application.paths["public"].first)
end
diff --git a/railties/lib/rails/app_rails_loader.rb b/railties/lib/rails/app_loader.rb
index 9a7c6c5f2d..a9fe21824e 100644
--- a/railties/lib/rails/app_rails_loader.rb
+++ b/railties/lib/rails/app_loader.rb
@@ -2,7 +2,7 @@ require 'pathname'
require 'rails/version'
module Rails
- module AppRailsLoader
+ module AppLoader # :nodoc:
extend self
RUBY = Gem.ruby
@@ -29,7 +29,7 @@ generate it and add it to source control:
EOS
- def exec_app_rails
+ def exec_app
original_cwd = Dir.pwd
loop do
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index a65f8f2ad9..8075068b3f 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -7,8 +7,7 @@ require 'active_support/message_verifier'
require 'rails/engine'
module Rails
- # In Rails 3.0, a Rails::Application object was introduced which is nothing more than
- # an Engine but with the responsibility of coordinating the whole boot process.
+ # An Engine with the responsibility of coordinating the whole boot process.
#
# == Initialization
#
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index dc3ec4274b..4fc7a1db62 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -11,12 +11,12 @@ module Rails
:eager_load, :exceptions_app, :file_watcher, :filter_parameters,
:force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags,
:railties_order, :relative_url_root, :secret_key_base, :secret_token,
- :serve_static_files, :ssl_options, :static_cache_control, :session_options,
- :time_zone, :reload_classes_only_on_change,
+ :serve_static_files, :ssl_options, :static_cache_control, :static_index,
+ :session_options, :time_zone, :reload_classes_only_on_change,
:beginning_of_week, :filter_redirect, :x
attr_writer :log_level
- attr_reader :encoding
+ attr_reader :encoding, :api_only
def initialize(*)
super
@@ -28,6 +28,7 @@ module Rails
@helpers_paths = []
@serve_static_files = true
@static_cache_control = nil
+ @static_index = "index"
@force_ssl = false
@ssl_options = {}
@session_store = :cookie_store
@@ -48,6 +49,7 @@ module Rails
@eager_load = nil
@secret_token = nil
@secret_key_base = nil
+ @api_only = false
@x = Custom.new
end
@@ -59,6 +61,11 @@ module Rails
end
end
+ def api_only=(value)
+ @api_only = value
+ generators.api_only = value
+ end
+
def paths
@paths ||= begin
paths = super
diff --git a/railties/lib/rails/application/default_middleware_stack.rb b/railties/lib/rails/application/default_middleware_stack.rb
index 02eea82b0c..6f9ccec137 100644
--- a/railties/lib/rails/application/default_middleware_stack.rb
+++ b/railties/lib/rails/application/default_middleware_stack.rb
@@ -18,7 +18,7 @@ module Rails
middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
if config.serve_static_files
- middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control
+ middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control, index: config.static_index
end
if rack_cache = load_rack_cache
@@ -28,7 +28,7 @@ module Rails
middleware.use ::Rack::Lock unless allow_concurrency?
middleware.use ::Rack::Runtime
- middleware.use ::Rack::MethodOverride
+ middleware.use ::Rack::MethodOverride unless config.api_only
middleware.use ::ActionDispatch::RequestId
# Must come after Rack::MethodOverride to properly log overridden methods
@@ -42,9 +42,9 @@ module Rails
end
middleware.use ::ActionDispatch::Callbacks
- middleware.use ::ActionDispatch::Cookies
+ middleware.use ::ActionDispatch::Cookies unless config.api_only
- if config.session_store
+ if !config.api_only && config.session_store
if config.force_ssl && !config.session_options.key?(:secure)
config.session_options[:secure] = true
end
diff --git a/railties/lib/rails/cli.rb b/railties/lib/rails/cli.rb
index dd70c272c6..a8794bc0de 100644
--- a/railties/lib/rails/cli.rb
+++ b/railties/lib/rails/cli.rb
@@ -1,8 +1,8 @@
-require 'rails/app_rails_loader'
+require 'rails/app_loader'
# If we are inside a Rails application this method performs an exec and thus
# the rest of this script is not run.
-Rails::AppRailsLoader.exec_app_rails
+Rails::AppLoader.exec_app
require 'rails/ruby_version_check'
Signal.trap("INT") { puts; exit(1) }
diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb
index 5d37a2b699..ea5d20ea24 100644
--- a/railties/lib/rails/commands/console.rb
+++ b/railties/lib/rails/commands/console.rb
@@ -1,14 +1,13 @@
require 'optparse'
require 'irb'
require 'irb/completion'
+require 'rails/commands/console_helper'
module Rails
class Console
- class << self
- def start(*args)
- new(*args).start
- end
+ include ConsoleHelper
+ class << self
def parse_arguments(arguments)
options = {}
@@ -21,23 +20,8 @@ module Rails
opt.parse!(arguments)
end
- if arguments.first && arguments.first[0] != '-'
- env = arguments.first
- if available_environments.include? env
- options[:environment] = env
- else
- options[:environment] = %w(production development test).detect {|e| e =~ /^#{env}/} || env
- end
- end
-
- options
+ set_options_env(arguments, options)
end
-
- private
-
- def available_environments
- Dir['config/environments/*.rb'].map { |fname| File.basename(fname, '.*') }
- end
end
attr_reader :options, :app, :console
@@ -57,12 +41,9 @@ module Rails
end
def environment
- options[:environment] ||= ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
- end
-
- def environment?
- environment
+ options[:environment] ||= super
end
+ alias_method :environment?, :environment
def set_environment!
Rails.env = environment
diff --git a/railties/lib/rails/commands/console_helper.rb b/railties/lib/rails/commands/console_helper.rb
new file mode 100644
index 0000000000..8ee0b60012
--- /dev/null
+++ b/railties/lib/rails/commands/console_helper.rb
@@ -0,0 +1,34 @@
+require 'active_support/concern'
+
+module Rails
+ module ConsoleHelper # :nodoc:
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def start(*args)
+ new(*args).start
+ end
+
+ private
+ def set_options_env(arguments, options)
+ if arguments.first && arguments.first[0] != '-'
+ env = arguments.first
+ 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
+ end
+
+ def environment
+ ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
+ end
+ end
+end \ No newline at end of file
diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb
index 3b22b582cf..dca60f948f 100644
--- a/railties/lib/rails/commands/dbconsole.rb
+++ b/railties/lib/rails/commands/dbconsole.rb
@@ -1,13 +1,49 @@
require 'erb'
require 'yaml'
require 'optparse'
+require 'rails/commands/console_helper'
module Rails
class DBConsole
+ include ConsoleHelper
+
attr_reader :arguments
- def self.start
- new.start
+ class << self
+ def parse_arguments(arguments)
+ options = {}
+
+ OptionParser.new do |opt|
+ opt.banner = "Usage: rails dbconsole [environment] [options]"
+ opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v|
+ options['include_password'] = true
+ end
+
+ opt.on("--mode [MODE]", ['html', 'list', 'line', 'column'],
+ "Automatically put the sqlite3 database in the specified mode (html, list, line, column).") do |mode|
+ options['mode'] = mode
+ end
+
+ opt.on("--header") do |h|
+ options['header'] = h
+ end
+
+ opt.on("-h", "--help", "Show this help message.") do
+ puts opt
+ exit
+ end
+
+ opt.on("-e", "--environment=name", String,
+ "Specifies the environment to run this console under (test/development/production).",
+ "Default: development"
+ ) { |v| options[:environment] = v.strip }
+
+ opt.parse!(arguments)
+ abort opt.to_s unless (0..1).include?(arguments.size)
+ end
+
+ set_options_env(arguments, options)
+ end
end
def initialize(arguments = ARGV)
@@ -15,7 +51,7 @@ module Rails
end
def start
- options = parse_arguments(arguments)
+ options = self.class.parse_arguments(arguments)
ENV['RAILS_ENV'] = options[:environment] || environment
case config["adapter"]
@@ -101,90 +137,37 @@ module Rails
end
def environment
- if Rails.respond_to?(:env)
- Rails.env
- else
- ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
- end
+ Rails.respond_to?(:env) ? Rails.env : super
end
protected
+ def configurations
+ require APP_PATH
+ ActiveRecord::Base.configurations = Rails.application.config.database_configuration
+ ActiveRecord::Base.configurations
+ end
- def configurations
- require APP_PATH
- ActiveRecord::Base.configurations = Rails.application.config.database_configuration
- ActiveRecord::Base.configurations
- end
-
- def parse_arguments(arguments)
- options = {}
-
- OptionParser.new do |opt|
- opt.banner = "Usage: rails dbconsole [environment] [options]"
- opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v|
- options['include_password'] = true
- end
+ def find_cmd_and_exec(commands, *args)
+ commands = Array(commands)
- opt.on("--mode [MODE]", ['html', 'list', 'line', 'column'],
- "Automatically put the sqlite3 database in the specified mode (html, list, line, column).") do |mode|
- options['mode'] = mode
+ dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR)
+ unless (ext = RbConfig::CONFIG['EXEEXT']).empty?
+ commands = commands.map{|cmd| "#{cmd}#{ext}"}
end
- opt.on("--header") do |h|
- options['header'] = h
+ full_path_command = nil
+ found = commands.detect do |cmd|
+ dirs_on_path.detect do |path|
+ full_path_command = File.join(path, cmd)
+ File.file?(full_path_command) && File.executable?(full_path_command)
+ end
end
- opt.on("-h", "--help", "Show this help message.") do
- puts opt
- exit
- end
-
- opt.on("-e", "--environment=name", String,
- "Specifies the environment to run this console under (test/development/production).",
- "Default: development"
- ) { |v| options[:environment] = v.strip }
-
- opt.parse!(arguments)
- abort opt.to_s unless (0..1).include?(arguments.size)
- end
-
- if arguments.first && arguments.first[0] != '-'
- env = arguments.first
- if available_environments.include? env
- options[:environment] = env
+ if found
+ exec full_path_command, *args
else
- options[:environment] = %w(production development test).detect {|e| e =~ /^#{env}/} || env
+ abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
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)
-
- dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR)
- unless (ext = RbConfig::CONFIG['EXEEXT']).empty?
- commands = commands.map{|cmd| "#{cmd}#{ext}"}
- end
-
- full_path_command = nil
- found = commands.detect do |cmd|
- dirs_on_path.detect do |path|
- full_path_command = File.join(path, cmd)
- File.file?(full_path_command) && File.executable?(full_path_command)
- end
- end
-
- if found
- exec full_path_command, *args
- else
- abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
- end
- end
end
end
diff --git a/railties/lib/rails/commands/test.rb b/railties/lib/rails/commands/test.rb
index 598e224a6f..fe5307788a 100644
--- a/railties/lib/rails/commands/test.rb
+++ b/railties/lib/rails/commands/test.rb
@@ -1,5 +1,5 @@
-require "rails/test_unit/runner"
+require "rails/test_unit/minitest_plugin"
$: << File.expand_path("../../test", APP_PATH)
-Rails::TestRunner.run(ARGV)
+exit Minitest.run(ARGV)
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index 76364cea8f..d99d27a756 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -74,7 +74,7 @@ module Rails
end
class Generators #:nodoc:
- attr_accessor :aliases, :options, :templates, :fallbacks, :colorize_logging
+ attr_accessor :aliases, :options, :templates, :fallbacks, :colorize_logging, :api_only
attr_reader :hidden_namespaces
def initialize
@@ -83,6 +83,7 @@ module Rails
@fallbacks = {}
@templates = []
@colorize_logging = true
+ @api_only = false
@hidden_namespaces = []
end
diff --git a/railties/lib/rails/console/app.rb b/railties/lib/rails/console/app.rb
index 2a69c26deb..ac5836a588 100644
--- a/railties/lib/rails/console/app.rb
+++ b/railties/lib/rails/console/app.rb
@@ -18,6 +18,11 @@ module Rails
app = Rails.application
session = ActionDispatch::Integration::Session.new(app)
yield session if block_given?
+
+ # This makes app.url_for and app.foo_path available in the console
+ session.extend(app.routes.url_helpers)
+ session.extend(app.routes.mounted_helpers)
+
session
end
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 9c287b3804..1dede32dd4 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -6,7 +6,7 @@ require 'pathname'
module Rails
# <tt>Rails::Engine</tt> allows you to wrap a specific Rails application or subset of
# functionality and share it with other applications or within a larger packaged application.
- # Since Rails 3.0, every <tt>Rails::Application</tt> is just an engine, which allows for simple
+ # Every <tt>Rails::Application</tt> is just an engine, which allows for simple
# feature and application sharing.
#
# Any <tt>Rails::Engine</tt> is also a <tt>Rails::Railtie</tt>, so the same
@@ -15,10 +15,9 @@ module Rails
#
# == Creating an Engine
#
- # In Rails versions prior to 3.0, your gems automatically behaved as engines, however,
- # this coupled Rails to Rubygems. Since Rails 3.0, if you want a gem to automatically
- # behave as an engine, you have to specify an +Engine+ for it somewhere inside
- # your plugin's +lib+ folder (similar to how we specify a +Railtie+):
+ # If you want a gem to behave as an engine, you have to specify an +Engine+
+ # for it somewhere inside your plugin's +lib+ folder (similar to how we
+ # specify a +Railtie+):
#
# # lib/my_engine.rb
# module MyEngine
@@ -69,10 +68,9 @@ module Rails
#
# == Paths
#
- # Since Rails 3.0, applications and engines have more flexible path configuration (as
- # opposed to the previous hardcoded path configuration). This means that you are not
- # required to place your controllers at <tt>app/controllers</tt>, but in any place
- # which you find convenient.
+ # Applications and engines have flexible path configuration, meaning that you
+ # are not required to place your controllers at <tt>app/controllers</tt>, but
+ # in any place which you find convenient.
#
# For example, let's suppose you want to place your controllers in <tt>lib/controllers</tt>.
# You can set that as an option:
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index a7da92168d..b430cf1909 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -33,6 +33,7 @@ module Rails
scaffold_controller: '-c',
stylesheets: '-y',
stylesheet_engine: '-se',
+ scaffold_stylesheet: '-ss',
template_engine: '-e',
test_framework: '-t'
},
@@ -44,6 +45,7 @@ module Rails
DEFAULT_OPTIONS = {
rails: {
+ api: false,
assets: true,
force_plural: false,
helper: true,
@@ -56,12 +58,14 @@ module Rails
scaffold_controller: :scaffold_controller,
stylesheets: true,
stylesheet_engine: :css,
+ scaffold_stylesheet: true,
test_framework: false,
template_engine: :erb
}
}
def self.configure!(config) #:nodoc:
+ api_only! if config.api_only
no_color! unless config.colorize_logging
aliases.deep_merge! config.aliases
options.deep_merge! config.options
@@ -99,6 +103,21 @@ module Rails
@fallbacks ||= {}
end
+ # Configure generators for API only applications. It basically hides
+ # everything that is usually browser related, such as assets and session
+ # migration generators, and completely disable views, helpers and assets
+ # so generators such as scaffold won't create them.
+ def self.api_only!
+ hide_namespaces "assets", "helper", "css", "js"
+
+ options[:rails].merge!(
+ api: true,
+ assets: false,
+ helper: false,
+ template_engine: nil
+ )
+ end
+
# Remove the color from output.
def self.no_color!
Thor::Base.shell = Thor::Shell::Basic
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 119a7cb829..249fe96772 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -174,6 +174,10 @@ module Rails
options[value] ? '# ' : ''
end
+ def keeps?
+ !options[:skip_keeps]
+ end
+
def sqlite3?
!options[:skip_active_record] && options[:database] == 'sqlite3'
end
@@ -204,11 +208,13 @@ module Rails
if options.dev?
[
GemfileEntry.path('rails', Rails::Generators::RAILS_DEV_PATH),
+ GemfileEntry.github('sprockets-rails', 'rails/sprockets-rails'),
GemfileEntry.github('arel', 'rails/arel')
]
elsif options.edge?
[
GemfileEntry.github('rails', 'rails/rails'),
+ GemfileEntry.github('sprockets-rails', 'rails/sprockets-rails'),
GemfileEntry.github('arel', 'rails/arel')
]
else
@@ -260,6 +266,8 @@ module Rails
end
def jbuilder_gemfile_entry
+ return [] if options[:api]
+
comment = 'Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder'
GemfileEntry.version('jbuilder', '~> 2.0', comment)
end
@@ -355,7 +363,7 @@ module Rails
end
def keep_file(destination)
- create_file("#{destination}/.keep") unless options[:skip_keeps]
+ create_file("#{destination}/.keep") if keeps?
end
end
end
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 1047a2c429..4b73313388 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -141,6 +141,7 @@ module Rails
end
def tmp
+ empty_directory_with_keep_file "tmp"
empty_directory "tmp/cache"
empty_directory "tmp/cache/assets"
end
@@ -174,6 +175,9 @@ module Rails
class_option :version, type: :boolean, aliases: "-v", group: :rails,
desc: "Show Rails version number and quit"
+ class_option :api, type: :boolean,
+ desc: "Preconfigure smaller stack for API only apps"
+
def initialize(*args)
super
@@ -184,6 +188,10 @@ module Rails
if !options[:skip_active_record] && !DATABASES.include?(options[:database])
raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}."
end
+
+ # Force sprockets to be skipped when generating API only apps.
+ # Can't modify options hash as it's frozen by default.
+ self.options = options.merge(skip_sprockets: true, skip_javascript: true).freeze if options[:api]
end
public_task :set_default_accessors!
@@ -251,6 +259,28 @@ module Rails
build(:vendor)
end
+ def delete_app_assets_if_api_option
+ if options[:api]
+ remove_dir 'app/assets'
+ remove_dir 'lib/assets'
+ remove_dir 'tmp/cache/assets'
+ remove_dir 'vendor/assets'
+ end
+ end
+
+ def delete_app_helpers_if_api_option
+ if options[:api]
+ remove_dir 'app/helpers'
+ remove_dir 'test/helpers'
+ end
+ end
+
+ def delete_app_views_if_api_option
+ if options[:api]
+ remove_dir 'app/views'
+ end
+ end
+
def delete_js_folder_skipping_javascript
if options[:skip_javascript]
remove_dir 'app/assets/javascripts'
@@ -269,6 +299,13 @@ module Rails
end
end
+ def delete_non_api_initializers_if_api_option
+ if options[:api]
+ remove_file 'config/initializers/session_store.rb'
+ remove_file 'config/initializers/cookies_serializer.rb'
+ end
+ end
+
def finish_template
build(:leftovers)
end
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index c11bb58bfa..606f1d4f96 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -21,11 +21,21 @@ source 'https://rubygems.org'
# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development
-group :development, :test do
+<%- if options.api? -%>
+# Use ActiveModelSerializers to serialize JSON responses
+gem 'active_model_serializers', '~> 0.10.0.rc1'
+
+# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
+# gem 'rack-cors'
+
+<%- end -%>
<% if RUBY_ENGINE == 'ruby' -%>
+group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug'
+end
+group :development do
# Access an IRB console on exception pages or by using <%%= console %> in views
<%- if options.dev? || options.edge? -%>
gem 'web-console', github: "rails/web-console"
@@ -36,8 +46,8 @@ group :development, :test do
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
<% end -%>
-<% end -%>
end
+<% end -%>
<% if RUBY_PLATFORM.match(/bccwin|cygwin|emx|mingw|mswin|wince|java/) -%>
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
diff --git a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
index d83690e1b9..f726fd6305 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
@@ -1,5 +1,7 @@
-class ApplicationController < ActionController::Base
+class ApplicationController < ActionController::<%= options[:api] ? "API" : "Base" %>
+<%- unless options[:api] -%>
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
+<%- end -%>
end
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/setup b/railties/lib/rails/generators/rails/app/templates/bin/setup
index 3a5a2cc1e3..0d41f2fe4c 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/setup
+++ b/railties/lib/rails/generators/rails/app/templates/bin/setup
@@ -25,5 +25,5 @@ chdir APP_ROOT do
system 'ruby bin/rake log:clear tmp:clear'
puts "\n== Restarting application server =="
- touch 'tmp/restart.txt'
+ system 'ruby bin/rake restart'
end
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 a2661bfb51..6b7d7abd0b 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -32,5 +32,12 @@ module <%= app_const_base %>
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
+<%- if options[:api] -%>
+
+ # Only loads a smaller set of middleware suitable for API only apps.
+ # Middleware like session, flash, cookies can be added back manually.
+ # Skip views, helpers and assets when generating a new resource.
+ config.api_only = true
+<%- end -%>
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb
new file mode 100644
index 0000000000..45c44d24f8
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/cors.rb
@@ -0,0 +1,14 @@
+# Avoid CORS issues when API is called from the frontend app
+# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests
+
+# Read more: https://github.com/cyu/rack-cors
+
+# Rails.application.config.middleware.insert_before 0, "Rack::Cors" do
+# allow do
+# origins 'example.com'
+#
+# resource '*',
+# headers: :any,
+# methods: [:get, :post, :put, :patch, :delete, :options, :head]
+# end
+# end
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 94f612c3dd..cadc85cfac 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
@@ -5,7 +5,7 @@
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
- wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
+ wrap_parameters format: [:json]
end
<%- unless options.skip_active_record? -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore
index 7c6f2098b8..1b8cf8a9fa 100644
--- a/railties/lib/rails/generators/rails/app/templates/gitignore
+++ b/railties/lib/rails/generators/rails/app/templates/gitignore
@@ -15,5 +15,8 @@
<% end -%>
# Ignore all logfiles and tempfiles.
/log/*
+/tmp/*
+<% if keeps? -%>
!/log/.keep
-/tmp
+!/tmp/.keep
+<% end -%>
diff --git a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
index c986f95e67..42705107ae 100644
--- a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
+++ b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
@@ -1,7 +1,6 @@
module Rails
module Generators
class ResourceRouteGenerator < NamedBase # :nodoc:
-
# Properly nests namespaces passed into a generator
#
# $ rails generate resource admin/users/products
diff --git a/railties/lib/rails/generators/rails/scaffold/USAGE b/railties/lib/rails/generators/rails/scaffold/USAGE
index 1b2a944103..d2e495758d 100644
--- a/railties/lib/rails/generators/rails/scaffold/USAGE
+++ b/railties/lib/rails/generators/rails/scaffold/USAGE
@@ -36,6 +36,6 @@ Description:
Examples:
`rails generate scaffold post`
- `rails generate scaffold post title body:text published:boolean`
+ `rails generate scaffold post title:string body:text published:boolean`
`rails generate scaffold purchase amount:decimal tracking_id:integer:uniq`
`rails generate scaffold user email:uniq password:digest`
diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
index e89789e72b..17c32bfdb3 100644
--- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
@@ -10,10 +10,11 @@ module Rails
class_option :stylesheet_engine, desc: "Engine for Stylesheets"
class_option :assets, type: :boolean
class_option :resource_route, type: :boolean
+ class_option :scaffold_stylesheet, type: :boolean
def handle_skip
@options = @options.merge(stylesheets: false) unless options[:assets]
- @options = @options.merge(stylesheet_engine: false) unless options[:stylesheets]
+ @options = @options.merge(stylesheet_engine: false) unless options[:stylesheets] && options[:scaffold_stylesheet]
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 c01b82884d..d0b8cad896 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,11 +10,14 @@ module Rails
class_option :helper, type: :boolean
class_option :orm, banner: "NAME", type: :string, required: true,
desc: "ORM to generate the controller for"
+ class_option :api, type: :boolean,
+ desc: "Generates API controller"
argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_controller_files
- template "controller.rb", File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
+ template_file = options.api? ? "api_controller.rb" : "controller.rb"
+ template template_file, File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
end
hook_for :template_engine, :test_framework, as: :scaffold
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
new file mode 100644
index 0000000000..bc3c9b3f6b
--- /dev/null
+++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/api_controller.rb
@@ -0,0 +1,61 @@
+<% if namespaced? -%>
+require_dependency "<%= namespaced_file_path %>/application_controller"
+
+<% end -%>
+<% module_namespacing do -%>
+class <%= controller_class_name %>Controller < ApplicationController
+ before_action :set_<%= singular_table_name %>, only: [:show, :update, :destroy]
+
+ # GET <%= route_url %>
+ def index
+ @<%= plural_table_name %> = <%= orm_class.all(class_name) %>
+
+ render json: <%= "@#{plural_table_name}" %>
+ end
+
+ # GET <%= route_url %>/1
+ def show
+ render json: <%= "@#{singular_table_name}" %>
+ end
+
+ # POST <%= route_url %>
+ def create
+ @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
+
+ if @<%= orm_instance.save %>
+ render json: <%= "@#{singular_table_name}" %>, status: :created, location: <%= "@#{singular_table_name}" %>
+ else
+ render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
+ end
+ end
+
+ # PATCH/PUT <%= route_url %>/1
+ def update
+ if @<%= orm_instance.update("#{singular_table_name}_params") %>
+ render json: <%= "@#{singular_table_name}" %>
+ else
+ render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
+ end
+ end
+
+ # DELETE <%= route_url %>/1
+ def destroy
+ @<%= orm_instance.destroy %>
+ end
+
+ private
+ # Use callbacks to share common setup or constraints between actions.
+ def set_<%= singular_table_name %>
+ @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
+ end
+
+ # Only allow a trusted parameter "white list" through.
+ def <%= "#{singular_table_name}_params" %>
+ <%- if attributes_names.empty? -%>
+ params[:<%= singular_table_name %>]
+ <%- else -%>
+ params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
+ <%- end -%>
+ end
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb
index 509bd60564..7106096b60 100644
--- a/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb
+++ b/railties/lib/rails/generators/test_unit/controller/templates/functional_test.rb
@@ -2,6 +2,12 @@ require 'test_helper'
<% module_namespacing do -%>
class <%= class_name %>ControllerTest < ActionController::TestCase
+<% if defined?(ENGINE_ROOT) -%>
+ setup do
+ @routes = Engine.routes
+ end
+
+<% end -%>
<% if actions.empty? -%>
# test "the truth" do
# assert true
diff --git a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
index 2e1f55f2a6..d634584beb 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
@@ -8,13 +8,26 @@ module TestUnit # :nodoc:
check_class_collision suffix: "ControllerTest"
+ class_option :api, type: :boolean,
+ desc: "Generates API functional tests"
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_test_files
- template "functional_test.rb",
+ template_file = options.api? ? "api_functional_test.rb" : "functional_test.rb"
+ template template_file,
File.join("test/controllers", controller_class_path, "#{controller_file_name}_controller_test.rb")
end
+ def fixture_name
+ @fixture_name ||=
+ if defined?(ENGINE_ROOT)
+ "%s_%s" % [namespaced_path, table_name]
+ else
+ table_name
+ end
+ end
+
private
def attributes_hash
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb
new file mode 100644
index 0000000000..896b38bc8f
--- /dev/null
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/api_functional_test.rb
@@ -0,0 +1,40 @@
+require 'test_helper'
+
+<% module_namespacing do -%>
+class <%= controller_class_name %>ControllerTest < ActionController::TestCase
+ setup do
+ @<%= singular_table_name %> = <%= table_name %>(:one)
+ end
+
+ test "should get index" do
+ get :index
+ assert_response :success
+ end
+
+ test "should create <%= singular_table_name %>" do
+ assert_difference('<%= class_name %>.count') do
+ post :create, params: { <%= "#{singular_table_name}: { #{attributes_hash} }" %> }
+ end
+
+ assert_response 201
+ end
+
+ test "should show <%= singular_table_name %>" do
+ get :show, params: { id: <%= "@#{singular_table_name}" %> }
+ assert_response :success
+ end
+
+ test "should update <%= singular_table_name %>" do
+ patch :update, params: { id: <%= "@#{singular_table_name}" %>, <%= "#{singular_table_name}: { #{attributes_hash} }" %> }
+ assert_response 200
+ end
+
+ test "should destroy <%= singular_table_name %>" do
+ assert_difference('<%= class_name %>.count', -1) do
+ delete :destroy, params: { id: <%= "@#{singular_table_name}" %> }
+ end
+
+ assert_response 204
+ end
+end
+<% end -%>
diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
index 76313575e9..d12a5ba733 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb
@@ -3,7 +3,10 @@ require 'test_helper'
<% module_namespacing do -%>
class <%= controller_class_name %>ControllerTest < ActionController::TestCase
setup do
- @<%= singular_table_name %> = <%= table_name %>(:one)
+ @<%= singular_table_name %> = <%= fixture_name %>(:one)
+<% if defined?(ENGINE_ROOT) -%>
+ @routes = Engine.routes
+<% end -%>
end
test "should get index" do
diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake
index a1c805f8aa..904b9d9ad6 100644
--- a/railties/lib/rails/tasks/framework.rake
+++ b/railties/lib/rails/tasks/framework.rake
@@ -32,35 +32,37 @@ namespace :rails do
FileUtils.cp_r src_name, dst_name
end
end
- end
+ end
end
namespace :update do
- def invoke_from_app_generator(method)
- app_generator.send(method)
- end
+ class RailsUpdate
+ def self.invoke_from_app_generator(method)
+ app_generator.send(method)
+ end
- def app_generator
- @app_generator ||= begin
- require 'rails/generators'
- require 'rails/generators/rails/app/app_generator'
- gen = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true },
- destination_root: Rails.root
- File.exist?(Rails.root.join("config", "application.rb")) ?
- gen.send(:app_const) : gen.send(:valid_const?)
- gen
+ def self.app_generator
+ @app_generator ||= begin
+ require 'rails/generators'
+ require 'rails/generators/rails/app/app_generator'
+ gen = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true },
+ destination_root: Rails.root
+ File.exist?(Rails.root.join("config", "application.rb")) ?
+ gen.send(:app_const) : gen.send(:valid_const?)
+ gen
+ end
end
end
# desc "Update config/boot.rb from your current rails install"
task :configs do
- invoke_from_app_generator :create_boot_file
- invoke_from_app_generator :update_config_files
+ RailsUpdate.invoke_from_app_generator :create_boot_file
+ RailsUpdate.invoke_from_app_generator :update_config_files
end
# desc "Adds new executables to the application bin/ directory"
task :bin do
- invoke_from_app_generator :create_bin_files
+ RailsUpdate.invoke_from_app_generator :create_bin_files
end
end
end
diff --git a/railties/lib/rails/tasks/restart.rake b/railties/lib/rails/tasks/restart.rake
index 1e8940b675..f36c86d81b 100644
--- a/railties/lib/rails/tasks/restart.rake
+++ b/railties/lib/rails/tasks/restart.rake
@@ -1,4 +1,5 @@
desc "Restart app by touching tmp/restart.txt"
task :restart do
+ FileUtils.mkdir_p('tmp')
FileUtils.touch('tmp/restart.txt')
end
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
index a83e39faee..3b444b0932 100644
--- a/railties/lib/rails/test_help.rb
+++ b/railties/lib/rails/test_help.rb
@@ -3,13 +3,17 @@
abort("Abort testing: Your Rails environment is running in production mode!") if Rails.env.production?
require "rails/test_unit/minitest_plugin"
-require 'active_support/testing/autorun'
require 'active_support/test_case'
require 'action_controller'
require 'action_controller/test_case'
require 'action_dispatch/testing/integration'
require 'rails/generators/test_case'
+unless Minitest.run_with_rails_extension
+ Minitest.run_with_autorun = true
+ require 'active_support/testing/autorun'
+end
+
if defined?(ActiveRecord::Base)
ActiveRecord::Migration.maintain_test_schema!
diff --git a/railties/lib/rails/test_unit/minitest_plugin.rb b/railties/lib/rails/test_unit/minitest_plugin.rb
index 70ce9d3360..421f032d81 100644
--- a/railties/lib/rails/test_unit/minitest_plugin.rb
+++ b/railties/lib/rails/test_unit/minitest_plugin.rb
@@ -1,14 +1,51 @@
-require "minitest"
+require "active_support/core_ext/module/attribute_accessors"
require "rails/test_unit/reporter"
+require "rails/test_unit/test_requirer"
-def Minitest.plugin_rails_init(options)
- self.reporter << Rails::TestUnitReporter.new(options[:io], options)
- if $rails_test_runner && (method = $rails_test_runner.find_method)
- options[:filter] = method
+module Minitest
+ def self.plugin_rails_options(opts, options)
+ opts.separator ""
+ opts.separator "Usage: bin/rails test [options] [files or directories]"
+ opts.separator "You can run a single test by appending a line number to a filename:"
+ opts.separator ""
+ opts.separator " bin/rails test test/models/user_test.rb:27"
+ opts.separator ""
+ opts.separator "You can run multiple files and directories at the same time:"
+ opts.separator ""
+ opts.separator " bin/rails test test/controllers test/integration/login_test.rb"
+ opts.separator ""
+
+ opts.separator "Rails options:"
+ opts.on("-e", "--environment [ENV]",
+ "Run tests in the ENV environment") do |env|
+ options[:environment] = env.strip
+ end
+
+ opts.on("-b", "--backtrace",
+ "Show the complete backtrace") do
+ options[:full_backtrace] = true
+ end
+
+ options[:patterns] = opts.order!
end
- if !($rails_test_runner && $rails_test_runner.show_backtrace?)
- Minitest.backtrace_filter = Rails.backtrace_cleaner
+ def self.plugin_rails_init(options)
+ self.run_with_rails_extension = true
+
+ ENV["RAILS_ENV"] = options[:environment] || "test"
+
+ Rails::TestRequirer.require_files options[:patterns] unless run_with_autorun
+
+ unless options[:full_backtrace] || ENV["BACKTRACE"]
+ # Plugin can run without Rails loaded, check before filtering.
+ Minitest.backtrace_filter = Rails.backtrace_cleaner if Rails.respond_to?(:backtrace_cleaner)
+ end
+
+ self.reporter << Rails::TestUnitReporter.new(options[:io], options)
end
+
+ mattr_accessor(:run_with_autorun) { false }
+ mattr_accessor(:run_with_rails_extension) { false }
end
+
Minitest.extensions << 'rails'
diff --git a/railties/lib/rails/test_unit/reporter.rb b/railties/lib/rails/test_unit/reporter.rb
index 64e99626eb..bfdc9754d4 100644
--- a/railties/lib/rails/test_unit/reporter.rb
+++ b/railties/lib/rails/test_unit/reporter.rb
@@ -15,8 +15,12 @@ module Rails
filtered_results.reject!(&:skipped?) unless options[:verbose]
filtered_results.map do |result|
location, line = result.method(result.name).source_location
- "bin/rails test #{location}:#{line}"
+ "bin/rails test #{relative_path_for(location)}:#{line}"
end.join "\n"
end
+
+ def relative_path_for(file)
+ file.sub(/^#{Rails.root}\/?/, '')
+ end
end
end
diff --git a/railties/lib/rails/test_unit/runner.rb b/railties/lib/rails/test_unit/runner.rb
deleted file mode 100644
index 5573fa6904..0000000000
--- a/railties/lib/rails/test_unit/runner.rb
+++ /dev/null
@@ -1,137 +0,0 @@
-require "optparse"
-require "rake/file_list"
-require "method_source"
-
-module Rails
- class TestRunner
- class Options
- def self.parse(args)
- options = { backtrace: !ENV["BACKTRACE"].nil?, name: nil, environment: "test" }
-
- opt_parser = ::OptionParser.new do |opts|
- opts.banner = "Usage: bin/rails test [options] [file or directory]"
-
- opts.separator ""
- opts.on("-e", "--environment [ENV]",
- "Run tests in the ENV environment") do |env|
- options[:environment] = env.strip
- end
- opts.separator ""
- opts.separator "Filter options:"
- opts.separator ""
- opts.separator <<-DESC
- You can run a single test by appending the line number to filename:
-
- bin/rails test test/models/user_test.rb:27
-
- DESC
-
- opts.on("-n", "--name [NAME]",
- "Only run tests matching NAME") do |name|
- options[:name] = name
- end
- opts.on("-p", "--pattern [PATTERN]",
- "Only run tests matching PATTERN") do |pattern|
- options[:name] = "/#{pattern}/"
- end
-
- opts.separator ""
- opts.separator "Output options:"
-
- opts.on("-b", "--backtrace",
- "Show the complete backtrace") do
- options[:backtrace] = true
- end
-
- opts.separator ""
- opts.separator "Common options:"
-
- opts.on_tail("-h", "--help", "Show this message") do
- puts opts
- exit
- end
- end
-
- opt_parser.order!(args)
-
- options[:patterns] = []
- while arg = args.shift
- if (file_and_line = arg.split(':')).size > 1
- options[:filename], options[:line] = file_and_line
- options[:filename] = File.expand_path options[:filename]
- options[:line] &&= options[:line].to_i
- else
- arg = arg.gsub(':', '')
- if Dir.exist?("#{arg}")
- options[:patterns] << File.expand_path("#{arg}/**/*_test.rb")
- elsif File.file?(arg)
- options[:patterns] << File.expand_path(arg)
- end
- end
- end
- options
- end
- end
-
- def initialize(options = {})
- @options = options
- end
-
- def self.run(arguments)
- options = Rails::TestRunner::Options.parse(arguments)
- Rails::TestRunner.new(options).run
- end
-
- def run
- $rails_test_runner = self
- ENV["RAILS_ENV"] = @options[:environment]
- run_tests
- end
-
- def find_method
- return @options[:name] if @options[:name]
- return unless @options[:line]
- method = test_methods.find do |location, test_method, start_line, end_line|
- location == @options[:filename] &&
- (start_line..end_line).include?(@options[:line].to_i)
- end
- method[1] if method
- end
-
- def show_backtrace?
- @options[:backtrace]
- end
-
- def test_files
- return [@options[:filename]] if @options[:filename]
- if @options[:patterns] && @options[:patterns].count > 0
- pattern = @options[:patterns]
- else
- pattern = "test/**/*_test.rb"
- end
- Rake::FileList[pattern]
- end
-
- private
- def run_tests
- test_files.to_a.each do |file|
- require File.expand_path file
- end
- end
-
- def test_methods
- methods_map = []
- suites = Minitest::Runnable.runnables.shuffle
- suites.each do |suite_class|
- suite_class.runnable_methods.each do |test_method|
- method = suite_class.instance_method(test_method)
- location = method.source_location
- start_line = location.last
- end_line = method.source.split("\n").size + start_line - 1
- methods_map << [File.expand_path(location.first), test_method, start_line, end_line]
- end
- end
- methods_map
- end
- end
-end
diff --git a/railties/lib/rails/test_unit/test_requirer.rb b/railties/lib/rails/test_unit/test_requirer.rb
new file mode 100644
index 0000000000..84c2256729
--- /dev/null
+++ b/railties/lib/rails/test_unit/test_requirer.rb
@@ -0,0 +1,28 @@
+require 'active_support/core_ext/object/blank'
+require 'rake/file_list'
+
+module Rails
+ class TestRequirer # :nodoc:
+ class << self
+ def require_files(patterns)
+ patterns = expand_patterns(patterns)
+
+ Rake::FileList[patterns.compact.presence || 'test/**/*_test.rb'].to_a.each do |file|
+ require File.expand_path(file)
+ end
+ end
+
+ private
+ def expand_patterns(patterns)
+ patterns.map do |arg|
+ arg = arg.gsub(/:(\d+)?$/, '')
+ if Dir.exist?(arg)
+ "#{arg}/**/*_test.rb"
+ elsif File.file?(arg)
+ arg
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 0f26621b59..dda492f974 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -1,12 +1,13 @@
-require "rails/test_unit/runner"
+gem 'minitest'
+require 'minitest'
+require 'rails/test_unit/minitest_plugin'
task default: :test
desc "Runs all tests in test folder"
task :test do
$: << "test"
- args = ARGV[0] == "test" ? ARGV[1..-1] : []
- Rails::TestRunner.run(args)
+ Minitest.run(['test'])
end
namespace :test do
@@ -23,22 +24,22 @@ namespace :test do
["models", "helpers", "controllers", "mailers", "integration", "jobs"].each do |name|
task name => "test:prepare" do
$: << "test"
- Rails::TestRunner.run(["test/#{name}"])
+ Minitest.run(["test/#{name}"])
end
end
task :generators => "test:prepare" do
$: << "test"
- Rails::TestRunner.run(["test/lib/generators"])
+ Minitest.run(["test/lib/generators"])
end
task :units => "test:prepare" do
$: << "test"
- Rails::TestRunner.run(["test/models", "test/helpers", "test/unit"])
+ Minitest.run(["test/models", "test/helpers", "test/unit"])
end
task :functionals => "test:prepare" do
$: << "test"
- Rails::TestRunner.run(["test/controllers", "test/mailers", "test/functional"])
+ Minitest.run(["test/controllers", "test/mailers", "test/functional"])
end
end
diff --git a/railties/test/app_rails_loader_test.rb b/railties/test/app_loader_test.rb
index d4885447e6..5946c8fd4c 100644
--- a/railties/test/app_rails_loader_test.rb
+++ b/railties/test/app_loader_test.rb
@@ -1,11 +1,11 @@
require 'tmpdir'
require 'abstract_unit'
-require 'rails/app_rails_loader'
+require 'rails/app_loader'
-class AppRailsLoaderTest < ActiveSupport::TestCase
+class AppLoaderTest < ActiveSupport::TestCase
def loader
@loader ||= Class.new do
- extend Rails::AppRailsLoader
+ extend Rails::AppLoader
def self.exec_arguments
@exec_arguments
@@ -23,7 +23,7 @@ class AppRailsLoaderTest < ActiveSupport::TestCase
end
def expects_exec(exe)
- assert_equal [Rails::AppRailsLoader::RUBY, exe], loader.exec_arguments
+ assert_equal [Rails::AppLoader::RUBY, exe], loader.exec_arguments
end
setup do
@@ -38,20 +38,20 @@ class AppRailsLoaderTest < ActiveSupport::TestCase
test "is not in a Rails application if #{exe} is not found in the current or parent directories" do
def loader.find_executables; end
- assert !loader.exec_app_rails
+ assert !loader.exec_app
end
test "is not in a Rails application if #{exe} exists but is a folder" do
FileUtils.mkdir_p(exe)
- assert !loader.exec_app_rails
+ assert !loader.exec_app
end
['APP_PATH', 'ENGINE_PATH'].each do |keyword|
test "is in a Rails application if #{exe} exists and contains #{keyword}" do
write exe, keyword
- loader.exec_app_rails
+ loader.exec_app
expects_exec exe
end
@@ -59,7 +59,7 @@ class AppRailsLoaderTest < ActiveSupport::TestCase
test "is not in a Rails application if #{exe} exists but doesn't contain #{keyword}" do
write exe
- assert !loader.exec_app_rails
+ assert !loader.exec_app
end
test "is in a Rails application if parent directory has #{exe} containing #{keyword} and chdirs to the root directory" do
@@ -68,7 +68,7 @@ class AppRailsLoaderTest < ActiveSupport::TestCase
Dir.chdir('foo/bar')
- loader.exec_app_rails
+ loader.exec_app
expects_exec exe
diff --git a/railties/test/application/configuration/custom_test.rb b/railties/test/application/configuration/custom_test.rb
index f5c99d030e..28b3b2f2d6 100644
--- a/railties/test/application/configuration/custom_test.rb
+++ b/railties/test/application/configuration/custom_test.rb
@@ -1,5 +1,4 @@
require 'isolation/abstract_unit'
-require 'env_helpers'
module ApplicationTests
module ConfigurationTests
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 38516a1c1a..f677a7c42a 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -745,6 +745,19 @@ module ApplicationTests
assert_equal [::MyMailObserver, ::MyOtherMailObserver], ::Mail.send(:class_variable_get, "@@delivery_notification_observers")
end
+ test "allows setting the queue name for the ActionMailer::DeliveryJob" do
+ add_to_config <<-RUBY
+ config.action_mailer.deliver_later_queue_name = 'test_default'
+ RUBY
+
+ require "#{app_path}/config/environment"
+ require "mail"
+
+ _ = ActionMailer::Base
+
+ assert_equal 'test_default', ActionMailer::Base.send(:class_variable_get, "@@deliver_later_queue_name")
+ end
+
test "valid timezone is setup correctly" do
add_to_config <<-RUBY
config.root = "#{app_path}"
diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb
index 31bc003dcb..7bf123d12b 100644
--- a/railties/test/application/console_test.rb
+++ b/railties/test/application/console_test.rb
@@ -29,6 +29,18 @@ class ConsoleTest < ActiveSupport::TestCase
assert_instance_of ActionDispatch::Integration::Session, console_session
end
+ def test_app_can_access_path_helper_method
+ app_file 'config/routes.rb', <<-RUBY
+ Rails.application.routes.draw do
+ get 'foo', to: 'foo#index'
+ end
+ RUBY
+
+ load_environment
+ console_session = irb_context.app
+ assert_equal '/foo', console_session.foo_path
+ end
+
def test_new_session_should_return_integration_session
load_environment
session = irb_context.new_session
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
index 78ada58ec8..84cc6e120b 100644
--- a/railties/test/application/generators_test.rb
+++ b/railties/test/application/generators_test.rb
@@ -125,5 +125,40 @@ module ApplicationTests
assert_equal expected, c.generators.options
end
end
+
+ test "api only generators hide assets, helper, js and css namespaces and set api option" do
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+
+ # Initialize the application
+ require "#{app_path}/config/environment"
+ Rails.application.load_generators
+
+ assert Rails::Generators.hidden_namespaces.include?("assets")
+ assert Rails::Generators.hidden_namespaces.include?("helper")
+ assert Rails::Generators.hidden_namespaces.include?("js")
+ assert Rails::Generators.hidden_namespaces.include?("css")
+ assert Rails::Generators.options[:rails][:api]
+ assert_equal false, Rails::Generators.options[:rails][:assets]
+ assert_equal false, Rails::Generators.options[:rails][:helper]
+ assert_nil Rails::Generators.options[:rails][:template_engine]
+ end
+
+ test "api only generators allow overriding generator options" do
+ add_to_config <<-RUBY
+ config.generators.helper = true
+ config.api_only = true
+ config.generators.template_engine = :my_template
+ RUBY
+
+ # Initialize the application
+ require "#{app_path}/config/environment"
+ Rails.application.load_generators
+
+ assert Rails::Generators.options[:rails][:api]
+ assert Rails::Generators.options[:rails][:helper]
+ assert_equal :my_template, Rails::Generators.options[:rails][:template_engine]
+ end
end
end
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 97b51911d9..af98e08d0e 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -129,6 +129,35 @@ module ApplicationTests
assert_equal "false", last_response.body
end
+ test "action_controller api executes using all the middleware stack" do
+ add_to_config "config.api_only = true"
+
+ app_file "app/controllers/application_controller.rb", <<-RUBY
+ class ApplicationController < ActionController::API
+ end
+ RUBY
+
+ app_file "app/controllers/omg_controller.rb", <<-RUBY
+ class OmgController < ApplicationController
+ def show
+ render json: { omg: 'omg' }
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ Rails.application.routes.draw do
+ get "/:controller(/:action)"
+ end
+ RUBY
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get 'omg/show'
+ assert_equal '{"omg":"omg"}', last_response.body
+ end
+
# AD
test "action_dispatch extensions are applied to ActionDispatch" do
add_to_config "config.action_dispatch.tld_length = 2"
diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb
index a8dc79d10a..25eadfc387 100644
--- a/railties/test/application/middleware/session_test.rb
+++ b/railties/test/application/middleware/session_test.rb
@@ -35,7 +35,7 @@ module ApplicationTests
flash[:notice] = "notice"
end
- render nothing: true
+ head :ok
end
end
@@ -60,7 +60,7 @@ module ApplicationTests
def write_session
session[:foo] = 1
- render nothing: true
+ head :ok
end
def read_session
@@ -101,7 +101,7 @@ module ApplicationTests
def write_cookie
cookies[:foo] = '1'
- render nothing: true
+ head :ok
end
def read_cookie
@@ -139,7 +139,7 @@ module ApplicationTests
class FooController < ActionController::Base
def write_session
session[:foo] = 1
- render nothing: true
+ head :ok
end
def read_session
@@ -184,7 +184,7 @@ module ApplicationTests
class FooController < ActionController::Base
def write_session
session[:foo] = 1
- render nothing: true
+ head :ok
end
def read_session
@@ -234,12 +234,12 @@ module ApplicationTests
def write_raw_session
# {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1}
cookies[:_myapp_session] = "BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJTE5NjVkOTU3MjBmZmZjMTIzOTQxYmRmYjdkMmU2ODcwBjsAVEkiCGZvbwY7AEZpBg==--315fb9931921a87ae7421aec96382f0294119749"
- render nothing: true
+ head :ok
end
def write_session
session[:foo] = session[:foo] + 1
- render nothing: true
+ head :ok
end
def read_session
@@ -293,12 +293,12 @@ module ApplicationTests
def write_raw_session
# {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1}
cookies[:_myapp_session] = "BAh7B0kiD3Nlc3Npb25faWQGOgZFRkkiJTE5NjVkOTU3MjBmZmZjMTIzOTQxYmRmYjdkMmU2ODcwBjsAVEkiCGZvbwY7AEZpBg==--315fb9931921a87ae7421aec96382f0294119749"
- render nothing: true
+ head :ok
end
def write_session
session[:foo] = session[:foo] + 1
- render nothing: true
+ head :ok
end
def read_session
diff --git a/railties/test/application/middleware/static_test.rb b/railties/test/application/middleware/static_test.rb
index 121c5d3321..1a46cd3568 100644
--- a/railties/test/application/middleware/static_test.rb
+++ b/railties/test/application/middleware/static_test.rb
@@ -26,5 +26,26 @@ module ApplicationTests
assert_not last_response.headers.has_key?('Cache-Control'), "Cache-Control should not be set"
end
+
+ test "static_index defaults to 'index'" do
+ app_file "public/index.html", "/index.html"
+
+ require "#{app_path}/config/environment"
+
+ get '/'
+
+ assert_equal "/index.html\n", last_response.body
+ end
+
+ test "static_index configurable" do
+ app_file "public/other-index.html", "/other-index.html"
+ add_to_config "config.static_index = 'other-index'"
+
+ require "#{app_path}/config/environment"
+
+ get '/'
+
+ assert_equal "/other-index.html\n", last_response.body
+ end
end
end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 04bd19784a..ce92ebbf66 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -50,6 +50,33 @@ module ApplicationTests
], middleware
end
+ test "api middleware stack" do
+ add_to_config "config.api_only = true"
+
+ boot!
+
+ assert_equal [
+ "Rack::Sendfile",
+ "ActionDispatch::Static",
+ "Rack::Lock",
+ "ActiveSupport::Cache::Strategy::LocalCache",
+ "Rack::Runtime",
+ "ActionDispatch::RequestId",
+ "Rails::Rack::Logger", # must come after Rack::MethodOverride to properly log overridden methods
+ "ActionDispatch::ShowExceptions",
+ "ActionDispatch::DebugExceptions",
+ "ActionDispatch::RemoteIp",
+ "ActionDispatch::Reloader",
+ "ActionDispatch::Callbacks",
+ "ActiveRecord::ConnectionAdapters::ConnectionManagement",
+ "ActiveRecord::QueryCache",
+ "ActionDispatch::ParamsParser",
+ "Rack::Head",
+ "Rack::ConditionalGet",
+ "Rack::ETag"
+ ], middleware
+ end
+
test "Rack::Cache is not included by default" do
boot!
diff --git a/railties/test/application/multiple_applications_test.rb b/railties/test/application/multiple_applications_test.rb
index cddc79cc85..f2770a9cb4 100644
--- a/railties/test/application/multiple_applications_test.rb
+++ b/railties/test/application/multiple_applications_test.rb
@@ -118,7 +118,7 @@ module ApplicationTests
assert_equal 0, run_count, "Without loading the initializers, the count should be 0"
# Set config.eager_load to false so that an eager_load warning doesn't pop up
- AppTemplate::Application.new { config.eager_load = false }.initialize!
+ AppTemplate::Application.create { config.eager_load = false }.initialize!
assert_equal 3, run_count, "There should have been three initializers that incremented the count"
end
diff --git a/railties/test/application/rake/framework_test.rb b/railties/test/application/rake/framework_test.rb
new file mode 100644
index 0000000000..ec57af79f6
--- /dev/null
+++ b/railties/test/application/rake/framework_test.rb
@@ -0,0 +1,48 @@
+require "isolation/abstract_unit"
+require "active_support/core_ext/string/strip"
+
+module ApplicationTests
+ module RakeTests
+ class FrameworkTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf("#{app_path}/config/environments")
+ end
+
+ def teardown
+ teardown_app
+ end
+
+ def load_tasks
+ require 'rake'
+ require 'rdoc/task'
+ require 'rake/testtask'
+
+ Rails.application.load_tasks
+ end
+
+ test 'requiring the rake task should not define method .app_generator on Object' do
+ require "#{app_path}/config/environment"
+
+ load_tasks
+
+ assert_raise NameError do
+ Object.method(:app_generator)
+ end
+ end
+
+ test 'requiring the rake task should not define method .invoke_from_app_generator on Object' do
+ require "#{app_path}/config/environment"
+
+ load_tasks
+
+ assert_raise NameError do
+ Object.method(:invoke_from_app_generator)
+ end
+ end
+ end
+ end
+end
diff --git a/railties/test/application/rake/restart_test.rb b/railties/test/application/rake/restart_test.rb
index 35099913fb..4cae199e6b 100644
--- a/railties/test/application/rake/restart_test.rb
+++ b/railties/test/application/rake/restart_test.rb
@@ -13,12 +13,12 @@ module ApplicationTests
def teardown
teardown_app
end
-
+
test 'rake restart touches tmp/restart.txt' do
Dir.chdir(app_path) do
`rake restart`
assert File.exist?("tmp/restart.txt")
-
+
prev_mtime = File.mtime("tmp/restart.txt")
sleep(1)
`rake restart`
@@ -26,6 +26,14 @@ module ApplicationTests
assert_not_equal prev_mtime, curr_mtime
end
end
+
+ test 'rake restart should work even if tmp folder does not exist' do
+ Dir.chdir(app_path) do
+ FileUtils.remove_dir('tmp')
+ `rake restart`
+ assert File.exist?('tmp/restart.txt')
+ end
+ end
end
end
end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index de14f269df..4c1913f0cc 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -194,6 +194,25 @@ module ApplicationTests
assert_no_match(/Errors running/, output)
end
+ def test_api_scaffold_tests_pass_by_default
+ add_to_config <<-RUBY
+ config.api_only = true
+ RUBY
+
+ app_file "app/controllers/application_controller.rb", <<-RUBY
+ class ApplicationController < ActionController::API
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) do
+ `rails generate scaffold user username:string password:string;
+ bundle exec rake db:migrate test`
+ end
+
+ assert_match(/5 runs, 7 assertions, 0 failures, 0 errors/, output)
+ assert_no_match(/Errors running/, output)
+ end
+
def test_scaffold_with_references_columns_tests_pass_when_belongs_to_is_optional
app_file "config/initializers/active_record_belongs_to_required_by_default.rb",
"Rails.application.config.active_record.belongs_to_required_by_default = false"
@@ -285,5 +304,12 @@ module ApplicationTests
assert_match(/Hello, World!/, output)
end
+
+ def test_tmp_clear_should_work_if_folder_missing
+ FileUtils.remove_dir("#{app_path}/tmp")
+ errormsg = Dir.chdir(app_path) { `bundle exec rake tmp:clear` }
+ assert_predicate $?, :success?
+ assert_empty errormsg
+ end
end
end
diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb
index c122b315c0..bbaab42a7f 100644
--- a/railties/test/application/test_runner_test.rb
+++ b/railties/test/application/test_runner_test.rb
@@ -1,9 +1,10 @@
require 'isolation/abstract_unit'
require 'active_support/core_ext/string/strip'
+require 'env_helpers'
module ApplicationTests
class TestRunnerTest < ActiveSupport::TestCase
- include ActiveSupport::Testing::Isolation
+ include ActiveSupport::Testing::Isolation, EnvHelpers
def setup
build_app
@@ -14,20 +15,6 @@ module ApplicationTests
teardown_app
end
- def test_run_in_test_environment
- app_file 'test/unit/env_test.rb', <<-RUBY
- require 'test_helper'
-
- class EnvTest < ActiveSupport::TestCase
- def test_env
- puts "Current Environment: \#{Rails.env}"
- end
- end
- RUBY
-
- assert_match "Current Environment: test", run_test_command('test/unit/env_test.rb')
- end
-
def test_run_single_file
create_test_file :models, 'foo'
create_test_file :models, 'bar'
@@ -187,7 +174,7 @@ module ApplicationTests
end
RUBY
- run_test_command('-p rikka test/unit/chu_2_koi_test.rb').tap do |output|
+ run_test_command('-n /rikka/ test/unit/chu_2_koi_test.rb').tap do |output|
assert_match "Rikka", output
assert_no_match "Sanae", output
end
@@ -229,24 +216,128 @@ module ApplicationTests
assert_match "development", run_test_command('test/unit/env_test.rb')
end
+ def test_run_in_test_environment_by_default
+ create_env_test
+
+ assert_match "Current Environment: test", run_test_command('test/unit/env_test.rb')
+ end
+
def test_run_different_environment
- env = "development"
- app_file 'test/unit/env_test.rb', <<-RUBY
+ create_env_test
+
+ assert_match "Current Environment: development",
+ run_test_command("-e development test/unit/env_test.rb")
+ end
+
+ def test_generated_scaffold_works_with_rails_test
+ create_scaffold
+ assert_match "0 failures, 0 errors, 0 skips", run_test_command('')
+ end
+
+ def test_run_multiple_folders
+ create_test_file :models, 'account'
+ create_test_file :controllers, 'accounts_controller'
+
+ run_test_command('test/models test/controllers').tap do |output|
+ assert_match 'AccountTest', output
+ assert_match 'AccountsControllerTest', output
+ assert_match '2 runs, 2 assertions, 0 failures, 0 errors, 0 skips', output
+ end
+ end
+
+ def test_run_with_ruby_command
+ app_file 'test/models/post_test.rb', <<-RUBY
require 'test_helper'
- class EnvTest < ActiveSupport::TestCase
- def test_env
- puts Rails.env
+ class PostTest < ActiveSupport::TestCase
+ test 'declarative syntax works' do
+ puts 'PostTest'
+ assert true
end
end
RUBY
- assert_match env, run_test_command("-e #{env} test/unit/env_test.rb")
+ Dir.chdir(app_path) do
+ `ruby -Itest test/models/post_test.rb`.tap do |output|
+ assert_match 'PostTest', output
+ assert_no_match 'is already defined in', output
+ end
+ end
end
- def test_generated_scaffold_works_with_rails_test
- create_scaffold
- assert_match "0 failures, 0 errors, 0 skips", run_test_command('')
+ def test_mix_files_and_line_filters
+ create_test_file :models, 'account'
+ app_file 'test/models/post_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class PostTest < ActiveSupport::TestCase
+ def test_post
+ puts 'PostTest'
+ assert true
+ end
+
+ def test_line_filter_does_not_run_this
+ assert true
+ end
+ end
+ RUBY
+
+ run_test_command('test/models/account_test.rb test/models/post_test.rb:4').tap do |output|
+ assert_match 'AccountTest', output
+ assert_match 'PostTest', output
+ assert_match '2 runs, 2 assertions', output
+ end
+ end
+
+ def test_multiple_line_filters
+ create_test_file :models, 'account'
+ create_test_file :models, 'post'
+
+ run_test_command('test/models/account_test.rb:4 test/models/post_test.rb:4').tap do |output|
+ assert_match 'AccountTest', output
+ assert_match 'PostTest', output
+ end
+ end
+
+ def test_line_filter_without_line_runs_all_tests
+ create_test_file :models, 'account'
+
+ run_test_command('test/models/account_test.rb:').tap do |output|
+ assert_match 'AccountTest', output
+ end
+ end
+
+ def test_shows_filtered_backtrace_by_default
+ create_backtrace_test
+
+ assert_match 'Rails::BacktraceCleaner', run_test_command('test/unit/backtrace_test.rb')
+ end
+
+ def test_backtrace_option
+ create_backtrace_test
+
+ assert_match 'Minitest::BacktraceFilter', run_test_command('test/unit/backtrace_test.rb -b')
+ assert_match 'Minitest::BacktraceFilter',
+ run_test_command('test/unit/backtrace_test.rb --backtrace')
+ end
+
+ def test_show_full_backtrace_using_backtrace_environment_variable
+ create_backtrace_test
+
+ switch_env 'BACKTRACE', 'true' do
+ assert_match 'Minitest::BacktraceFilter', run_test_command('test/unit/backtrace_test.rb')
+ end
+ end
+
+ def test_run_app_without_rails_loaded
+ # Simulate a real Rails app boot.
+ app_file 'config/boot.rb', <<-RUBY
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
+
+ require 'bundler/setup' # Set up gems listed in the Gemfile.
+ RUBY
+
+ assert_match '0 runs, 0 assertions', run_test_command('')
end
private
@@ -284,6 +375,18 @@ module ApplicationTests
RUBY
end
+ def create_backtrace_test
+ app_file 'test/unit/backtrace_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class BacktraceTest < ActiveSupport::TestCase
+ def test_backtrace
+ puts Minitest.backtrace_filter
+ end
+ end
+ RUBY
+ end
+
def create_schema
app_file 'db/schema.rb', ''
end
@@ -301,6 +404,18 @@ module ApplicationTests
RUBY
end
+ def create_env_test
+ app_file 'test/unit/env_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class EnvTest < ActiveSupport::TestCase
+ def test_env
+ puts "Current Environment: \#{Rails.env}"
+ end
+ end
+ RUBY
+ end
+
def create_scaffold
script 'generate scaffold user name:string'
Dir.chdir(app_path) { File.exist?('app/models/user.rb') }
diff --git a/railties/test/application/test_test.rb b/railties/test/application/test_test.rb
index 61652e5052..0e997f4ba7 100644
--- a/railties/test/application/test_test.rb
+++ b/railties/test/application/test_test.rb
@@ -44,7 +44,7 @@ module ApplicationTests
def test_index
get '/posts'
assert_response :success
- assert_template "index"
+ assert_includes @response.body, 'Posts#index'
end
end
RUBY
@@ -64,8 +64,8 @@ module ApplicationTests
RUBY
output = run_test_file('unit/failing_test.rb', env: { "BACKTRACE" => "1" })
- assert_match %r{/app/test/unit/failing_test\.rb}, output
- assert_match %r{/app/test/unit/failing_test\.rb:4}, output
+ assert_match %r{test/unit/failing_test\.rb}, output
+ assert_match %r{test/unit/failing_test\.rb:4}, output
end
test "ruby schema migrations" do
diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb
index 2e8dd99f36..7950ed6aa7 100644
--- a/railties/test/commands/dbconsole_test.rb
+++ b/railties/test/commands/dbconsole_test.rb
@@ -99,19 +99,15 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase
end
def test_rails_env_is_development_when_argument_is_dev
- dbconsole = Rails::DBConsole.new
-
- dbconsole.stub(:available_environments, ['development', 'test']) do
- options = dbconsole.send(:parse_arguments, ['dev'])
+ Rails::DBConsole.stub(:available_environments, ['development', 'test']) do
+ options = Rails::DBConsole.send(:parse_arguments, ['dev'])
assert_match('development', options[:environment])
end
end
def test_rails_env_is_dev_when_argument_is_dev_and_dev_env_is_present
- dbconsole = Rails::DBConsole.new
-
- dbconsole.stub(:available_environments, ['dev']) do
- options = dbconsole.send(:parse_arguments, ['dev'])
+ Rails::DBConsole.stub(:available_environments, ['dev']) do
+ options = Rails::DBConsole.send(:parse_arguments, ['dev'])
assert_match('dev', options[:environment])
end
end
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index c0b88089b3..4a4317c4f4 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -1,7 +1,7 @@
require 'generators/generators_test_helper'
require 'rails/generators/rails/app/app_generator'
require 'env_helpers'
-require 'mocha/setup' # FIXME: stop using mocha
+require 'minitest/mock'
class ActionsTest < Rails::Generators::TestCase
include GeneratorsTestHelper
@@ -12,11 +12,13 @@ class ActionsTest < Rails::Generators::TestCase
def setup
Rails.application = TestApp::Application
+ @mock_generator = Minitest::Mock.new
super
end
def teardown
Rails.application = TestApp::Application.instance
+ @mock_generator.verify
end
def test_invoke_other_generator_with_shortcut
@@ -140,13 +142,18 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_git_with_symbol_should_run_command_using_git_scm
- generator.expects(:run).once.with('git init')
- action :git, :init
+ @mock_generator.expect(:call, nil, ['git init'])
+ generator.stub(:run, @mock_generator) do
+ action :git, :init
+ end
end
def test_git_with_hash_should_run_each_command_using_git_scm
- generator.expects(:run).times(2)
- action :git, rm: 'README', add: '.'
+ @mock_generator.expect(:call, nil, ["git rm README"])
+ @mock_generator.expect(:call, nil, ["git add ."])
+ generator.stub(:run, @mock_generator) do
+ action :git, rm: 'README', add: '.'
+ end
end
def test_vendor_should_write_data_to_file_in_vendor
@@ -170,46 +177,60 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_generate_should_run_script_generate_with_argument_and_options
- generator.expects(:run_ruby_script).once.with('bin/rails generate model MyModel', verbose: false)
- action :generate, 'model', 'MyModel'
+ @mock_generator.expect(:call, nil, ['bin/rails generate model MyModel', verbose: false])
+ generator.stub(:run_ruby_script, @mock_generator) do
+ action :generate, 'model', 'MyModel'
+ end
end
def test_rake_should_run_rake_command_with_default_env
- generator.expects(:run).once.with("rake log:clear RAILS_ENV=development", verbose: false)
- with_rails_env nil do
- action :rake, 'log:clear'
+ @mock_generator.expect(:call, nil, ["rake log:clear RAILS_ENV=development", verbose: false])
+ generator.stub(:run, @mock_generator) do
+ with_rails_env nil do
+ action :rake, 'log:clear'
+ end
end
end
def test_rake_with_env_option_should_run_rake_command_in_env
- generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', verbose: false)
- action :rake, 'log:clear', env: 'production'
+ @mock_generator.expect(:call, nil, ['rake log:clear RAILS_ENV=production', verbose: false])
+ generator.stub(:run, @mock_generator) do
+ action :rake, 'log:clear', env: 'production'
+ end
end
def test_rake_with_rails_env_variable_should_run_rake_command_in_env
- generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', verbose: false)
- with_rails_env "production" do
- action :rake, 'log:clear'
+ @mock_generator.expect(:call, nil, ['rake log:clear RAILS_ENV=production', verbose: false])
+ generator.stub(:run, @mock_generator) do
+ with_rails_env "production" do
+ action :rake, 'log:clear'
+ end
end
end
def test_env_option_should_win_over_rails_env_variable_when_running_rake
- generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', verbose: false)
- with_rails_env "staging" do
- action :rake, 'log:clear', env: 'production'
+ @mock_generator.expect(:call, nil, ['rake log:clear RAILS_ENV=production', verbose: false])
+ generator.stub(:run, @mock_generator) do
+ with_rails_env "staging" do
+ action :rake, 'log:clear', env: 'production'
+ end
end
end
def test_rake_with_sudo_option_should_run_rake_command_with_sudo
- generator.expects(:run).once.with("sudo rake log:clear RAILS_ENV=development", verbose: false)
- with_rails_env nil do
- action :rake, 'log:clear', sudo: true
+ @mock_generator.expect(:call, nil, ["sudo rake log:clear RAILS_ENV=development", verbose: false])
+ generator.stub(:run, @mock_generator) do
+ with_rails_env nil do
+ action :rake, 'log:clear', sudo: true
+ end
end
end
def test_capify_should_run_the_capify_command
- generator.expects(:run).once.with('capify .', verbose: false)
- action :capify!
+ @mock_generator.expect(:call, nil, ['capify .', verbose: false])
+ generator.stub(:run, @mock_generator) do
+ action :capify!
+ end
end
def test_route_should_add_data_to_the_routes_block_in_config_routes
@@ -245,15 +266,19 @@ F
def test_readme
run_generator
- Rails::Generators::AppGenerator.expects(:source_root).times(2).returns(destination_root)
- assert_match "application up and running", action(:readme, "README.md")
+ 2.times { @mock_generator.expect(:call, destination_root,[]) }
+ Rails::Generators::AppGenerator.stub(:source_root, @mock_generator) do
+ assert_match "application up and running", action(:readme, "README.md")
+ end
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 "application up and running", action(:readme, "README.md")
+ 2.times { @mock_generator.expect(:call, destination_root,[]) }
+ Rails::Generators::AppGenerator.stub(:source_root, @mock_generator) do
+ assert_no_match "application up and running", action(:readme, "README.md")
+ end
end
def test_log
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
new file mode 100644
index 0000000000..9978ad0da1
--- /dev/null
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -0,0 +1,96 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/app/app_generator'
+
+class ApiAppGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ tests Rails::Generators::AppGenerator
+
+ arguments [destination_root, '--api']
+
+ def setup
+ Rails.application = TestApp::Application
+ super
+
+ Kernel::silence_warnings do
+ Thor::Base.shell.send(:attr_accessor, :always_force)
+ @shell = Thor::Base.shell.new
+ @shell.send(:always_force=, true)
+ end
+ end
+
+ def teardown
+ super
+ Rails.application = TestApp::Application.instance
+ end
+
+ def test_skeleton_is_created
+ run_generator
+
+ default_files.each { |path| assert_file path }
+ skipped_files.each { |path| assert_no_file path }
+ end
+
+ def test_api_modified_files
+ run_generator
+
+ assert_file "Gemfile" do |content|
+ assert_no_match(/gem 'coffee-rails'/, content)
+ assert_no_match(/gem 'jquery-rails'/, content)
+ assert_no_match(/gem 'sass-rails'/, content)
+ assert_no_match(/gem 'jbuilder'/, content)
+ assert_match(/gem 'active_model_serializers'/, content)
+ end
+
+ assert_file "config/application.rb" do |content|
+ assert_match(/config.api_only = true/, content)
+ end
+
+ assert_file "config/initializers/cors.rb"
+
+ assert_file "config/initializers/wrap_parameters.rb"
+
+ assert_file "app/controllers/application_controller.rb", /ActionController::API/
+ end
+
+ private
+
+ def default_files
+ files = %W(
+ .gitignore
+ Gemfile
+ Rakefile
+ config.ru
+ app/controllers
+ app/mailers
+ app/models
+ config/environments
+ config/initializers
+ config/locales
+ db
+ lib
+ lib/tasks
+ log
+ test/fixtures
+ test/controllers
+ test/integration
+ test/models
+ tmp
+ vendor
+ )
+ files.concat %w(bin/bundle bin/rails bin/rake)
+ files
+ end
+
+ def skipped_files
+ %w(app/assets
+ app/helpers
+ app/views
+ config/initializers/assets.rb
+ config/initializers/cookies_serializer.rb
+ config/initializers/session_store.rb
+ lib/assets
+ vendor/assets
+ test/helpers
+ tmp/cache/assets)
+ end
+end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 2bfa05a0b8..af1c05cab1 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -44,6 +44,7 @@ DEFAULT_APP_FILES = %w(
vendor/assets
vendor/assets/stylesheets
vendor/assets/javascripts
+ tmp
tmp/cache
tmp/cache/assets
)
@@ -606,6 +607,32 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
end
+ def test_create_keeps
+ run_generator
+ folders_with_keep = %w(
+ app/assets/images
+ app/mailers
+ app/models
+ app/controllers/concerns
+ app/models/concerns
+ lib/tasks
+ lib/assets
+ log
+ test/fixtures
+ test/fixtures/files
+ test/controllers
+ test/mailers
+ test/models
+ test/helpers
+ test/integration
+ tmp
+ vendor/assets/stylesheets
+ )
+ folders_with_keep.each do |folder|
+ assert_file("#{folder}/.keep")
+ end
+ end
+
def test_psych_gem
run_generator
gem_regex = /gem 'psych',\s+'~> 2.0',\s+platforms: :rbx/
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index 34e752cea1..5dae36b65e 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -174,4 +174,62 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
end
end
end
+
+ def test_controller_tests_pass_by_default_inside_mountable_engine
+ Dir.chdir(destination_root) { `bundle exec rails plugin new bukkits --mountable` }
+
+ engine_path = File.join(destination_root, "bukkits")
+
+ Dir.chdir(engine_path) do
+ quietly { `bin/rails g controller dashboard foo` }
+ assert_match(/2 runs, 2 assertions, 0 failures, 0 errors/, `bundle exec rake test 2>&1`)
+ end
+ end
+
+ def test_api_only_generates_a_proper_api_controller
+ run_generator ["User", "--api"]
+
+ assert_file "app/controllers/users_controller.rb" do |content|
+ assert_match(/class UsersController < ApplicationController/, content)
+ assert_no_match(/respond_to/, content)
+
+ assert_match(/before_action :set_user, only: \[:show, :update, :destroy\]/, content)
+
+ assert_instance_method :index, content do |m|
+ assert_match(/@users = User\.all/, m)
+ assert_match(/render json: @users/, m)
+ end
+
+ assert_instance_method :show, content do |m|
+ assert_match(/render json: @user/, m)
+ end
+
+ 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|
+ assert_match(/@user\.destroy/, m)
+ end
+ end
+ end
+
+ def test_api_controller_tests
+ run_generator ["User", "name:string", "age:integer", "organization:references{polymorphic}", "--api"]
+
+ assert_file "test/controllers/users_controller_test.rb" do |content|
+ assert_match(/class UsersControllerTest < ActionController::TestCase/, content)
+ assert_match(/test "should get index"/, content)
+ assert_match(/post :create, params: \{ user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content)
+ assert_match(/patch :update, params: \{ id: @user, user: \{ age: @user\.age, name: @user\.name, organization_id: @user\.organization_id, organization_type: @user\.organization_type \} \}/, content)
+ assert_no_match(/assert_redirected_to/, content)
+ end
+ end
end
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index ee06802874..3401b96d7d 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -87,6 +87,76 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_file "app/assets/stylesheets/product_lines.css"
end
+ def test_api_scaffold_on_invoke
+ run_generator %w(product_line title:string product:belongs_to user:references --api --no-template-engine --no-helper --no-assets)
+
+ # Model
+ assert_file "app/models/product_line.rb", /class ProductLine < ActiveRecord::Base/
+ assert_file "test/models/product_line_test.rb", /class ProductLineTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/product_lines.yml"
+ assert_migration "db/migrate/create_product_lines.rb", /belongs_to :product, index: true/
+ assert_migration "db/migrate/create_product_lines.rb", /references :user, index: true/
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/resources :product_lines$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/product_lines_controller.rb" do |content|
+ assert_match(/class ProductLinesController < ApplicationController/, content)
+ assert_no_match(/respond_to/, content)
+
+ assert_match(/before_action :set_product_line, only: \[:show, :update, :destroy\]/, content)
+
+ assert_instance_method :index, content do |m|
+ assert_match(/@product_lines = ProductLine\.all/, m)
+ assert_match(/render json: @product_lines/, m)
+ end
+
+ assert_instance_method :show, content do |m|
+ assert_match(/render json: @product_line/, m)
+ end
+
+ 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|
+ assert_match(/@product_line\.destroy/, m)
+ end
+ end
+
+ assert_file "test/controllers/product_lines_controller_test.rb" do |test|
+ assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, test)
+ assert_match(/post :create, params: \{ product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test)
+ assert_match(/patch :update, params: \{ id: @product_line, product_line: \{ product_id: @product_line\.product_id, title: @product_line\.title, user_id: @product_line\.user_id \} \}/, test)
+ assert_no_match(/assert_redirected_to/, test)
+ end
+
+ # Views
+ assert_no_file "app/views/layouts/product_lines.html.erb"
+
+ %w(index show new edit _form).each do |view|
+ assert_no_file "app/views/product_lines/#{view}.html.erb"
+ end
+
+ # Helpers
+ assert_no_file "app/helpers/product_lines_helper.rb"
+
+ # Assets
+ assert_no_file "app/assets/stylesheets/scaffold.css"
+ assert_no_file "app/assets/javascripts/product_lines.js"
+ assert_no_file "app/assets/stylesheets/product_lines.css"
+ end
+
def test_functional_tests_without_attributes
run_generator ["product_line"]
@@ -282,6 +352,20 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_no_file "app/assets/stylesheets/posts.css"
end
+ def test_scaffold_generator_no_scaffold_stylesheet_with_switch_no_scaffold_stylesheet
+ run_generator [ "posts", "--no-scaffold-stylesheet" ]
+ assert_no_file "app/assets/stylesheets/scaffold.css"
+ assert_file "app/assets/javascripts/posts.js"
+ assert_file "app/assets/stylesheets/posts.css"
+ end
+
+ def test_scaffold_generator_no_scaffold_stylesheet_with_switch_scaffold_stylesheet_false
+ run_generator [ "posts", "--scaffold-stylesheet=false" ]
+ assert_no_file "app/assets/stylesheets/scaffold.css"
+ assert_file "app/assets/javascripts/posts.js"
+ assert_file "app/assets/stylesheets/posts.css"
+ end
+
def test_scaffold_generator_with_switch_resource_route_false
run_generator [ "posts", "--resource-route=false" ]
assert_file "config/routes.rb" do |route|
@@ -393,4 +477,18 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_match(/password_digest: <%= BCrypt::Password.create\('secret'\) %>/, content)
end
end
+
+ def test_scaffold_tests_pass_by_default_inside_mountable_engine
+ Dir.chdir(destination_root) { `bundle exec rails plugin new bukkits --mountable` }
+
+ engine_path = File.join(destination_root, "bukkits")
+
+ Dir.chdir(engine_path) do
+ quietly do
+ `bin/rails g scaffold User name:string age:integer;
+ bundle exec rake db:migrate`
+ end
+ assert_match(/8 runs, 13 assertions, 0 failures, 0 errors/, `bundle exec rake test 2>&1`)
+ end
+ end
end
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index 3bcf3894ce..f983b45d2b 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -86,15 +86,6 @@ module SharedGeneratorTests
end
end
- def test_template_is_executed_when_supplied
- path = "https://gist.github.com/josevalim/103208/raw/"
- template = %{ say "It works!" }
- template.instance_eval "def read; self; end" # Make the string respond to read
-
- generator([destination_root], template: path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template)
- quietly { assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) }
- end
-
def test_template_is_executed_when_supplied_an_https_path
path = "https://gist.github.com/josevalim/103208/raw/"
template = %{ say "It works!" }
@@ -138,7 +129,11 @@ module SharedGeneratorTests
def test_skip_keeps
run_generator [destination_root, '--skip-keeps', '--full']
- assert_file('.gitignore')
+
+ assert_file '.gitignore' do |content|
+ assert_no_match(/\.keep/, content)
+ end
+
assert_no_file('app/mailers/.keep')
end
end
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index ce75aba4eb..31a575749a 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -1,7 +1,7 @@
require 'generators/generators_test_helper'
require 'rails/generators/rails/model/model_generator'
require 'rails/generators/test_unit/model/model_generator'
-require 'mocha/setup' # FIXME: stop using mocha
+require 'minitest/mock'
class GeneratorsTest < Rails::Generators::TestCase
include GeneratorsTestHelper
@@ -9,16 +9,20 @@ class GeneratorsTest < Rails::Generators::TestCase
def setup
@path = File.expand_path("lib", Rails.root)
$LOAD_PATH.unshift(@path)
+ @mock_generator = MiniTest::Mock.new
end
def teardown
$LOAD_PATH.delete(@path)
+ @mock_generator.verify
end
def test_simple_invoke
assert File.exist?(File.join(@path, 'generators', 'model_generator.rb'))
- TestUnit::Generators::ModelGenerator.expects(:start).with(["Account"], {})
- Rails::Generators.invoke("test_unit:model", ["Account"])
+ @mock_generator.expect(:call, nil, [["Account"],{}])
+ TestUnit::Generators::ModelGenerator.stub(:start, @mock_generator) do
+ Rails::Generators.invoke("test_unit:model", ["Account"])
+ end
end
def test_invoke_when_generator_is_not_found
@@ -47,19 +51,25 @@ class GeneratorsTest < Rails::Generators::TestCase
def test_should_give_higher_preference_to_rails_generators
assert File.exist?(File.join(@path, 'generators', 'model_generator.rb'))
- Rails::Generators::ModelGenerator.expects(:start).with(["Account"], {})
- warnings = capture(:stderr){ Rails::Generators.invoke :model, ["Account"] }
- assert warnings.empty?
+ @mock_generator.expect(:call, nil, [["Account"],{}])
+ Rails::Generators::ModelGenerator.stub(:start, @mock_generator) do
+ warnings = capture(:stderr){ Rails::Generators.invoke :model, ["Account"] }
+ assert warnings.empty?
+ end
end
def test_invoke_with_default_values
- Rails::Generators::ModelGenerator.expects(:start).with(["Account"], {})
- Rails::Generators.invoke :model, ["Account"]
+ @mock_generator.expect(:call, nil, [["Account"],{}])
+ Rails::Generators::ModelGenerator.stub(:start, @mock_generator) do
+ Rails::Generators.invoke :model, ["Account"]
+ end
end
def test_invoke_with_config_values
- Rails::Generators::ModelGenerator.expects(:start).with(["Account"], behavior: :skip)
- Rails::Generators.invoke :model, ["Account"], behavior: :skip
+ @mock_generator.expect(:call, nil, [["Account"],{behavior: :skip}])
+ Rails::Generators::ModelGenerator.stub(:start, @mock_generator) do
+ Rails::Generators.invoke :model, ["Account"], behavior: :skip
+ end
end
def test_find_by_namespace
@@ -103,11 +113,13 @@ class GeneratorsTest < Rails::Generators::TestCase
end
def test_invoke_with_nested_namespaces
- model_generator = mock('ModelGenerator') do
- expects(:start).with(["Account"], {})
+ model_generator = Minitest::Mock.new
+ model_generator.expect(:start, nil, [["Account"], {}])
+ @mock_generator.expect(:call, model_generator, ['namespace', 'my:awesome'])
+ Rails::Generators.stub(:find_by_namespace, @mock_generator) do
+ Rails::Generators.invoke 'my:awesome:namespace', ["Account"]
end
- Rails::Generators.expects(:find_by_namespace).with('namespace', 'my:awesome').returns(model_generator)
- Rails::Generators.invoke 'my:awesome:namespace', ["Account"]
+ model_generator.verify
end
def test_rails_generators_help_with_builtin_information
@@ -173,8 +185,10 @@ class GeneratorsTest < Rails::Generators::TestCase
def test_fallbacks_for_generators_on_invoke
Rails::Generators.fallbacks[:shoulda] = :test_unit
- TestUnit::Generators::ModelGenerator.expects(:start).with(["Account"], {})
- Rails::Generators.invoke "shoulda:model", ["Account"]
+ @mock_generator.expect(:call, nil, [["Account"],{}])
+ TestUnit::Generators::ModelGenerator.stub(:start, @mock_generator) do
+ Rails::Generators.invoke "shoulda:model", ["Account"]
+ end
ensure
Rails::Generators.fallbacks.delete(:shoulda)
end
@@ -182,8 +196,10 @@ class GeneratorsTest < Rails::Generators::TestCase
def test_nested_fallbacks_for_generators
Rails::Generators.fallbacks[:shoulda] = :test_unit
Rails::Generators.fallbacks[:super_shoulda] = :shoulda
- TestUnit::Generators::ModelGenerator.expects(:start).with(["Account"], {})
- Rails::Generators.invoke "super_shoulda:model", ["Account"]
+ @mock_generator.expect(:call, nil, [["Account"],{}])
+ TestUnit::Generators::ModelGenerator.stub(:start, @mock_generator) do
+ Rails::Generators.invoke "super_shoulda:model", ["Account"]
+ end
ensure
Rails::Generators.fallbacks.delete(:shoulda)
Rails::Generators.fallbacks.delete(:super_shoulda)
diff --git a/railties/test/path_generation_test.rb b/railties/test/path_generation_test.rb
index d22374a1ff..27e64b97b7 100644
--- a/railties/test/path_generation_test.rb
+++ b/railties/test/path_generation_test.rb
@@ -1,8 +1,6 @@
require 'abstract_unit'
require 'active_support/core_ext/object/with_options'
require 'active_support/core_ext/object/json'
-require 'rails'
-require 'rails/application'
class PathGenerationTest < ActiveSupport::TestCase
attr_reader :app
diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb
index 1aeb9ec339..12630e4d01 100644
--- a/railties/test/paths_test.rb
+++ b/railties/test/paths_test.rb
@@ -1,10 +1,9 @@
require 'abstract_unit'
require 'rails/paths'
-require 'mocha/setup' # FIXME: stop using mocha
+require 'minitest/mock'
class PathsTest < ActiveSupport::TestCase
def setup
- File.stubs(:exist?).returns(true)
@root = Rails::Paths::Root.new("/foo/bar")
end
@@ -93,10 +92,12 @@ class PathsTest < ActiveSupport::TestCase
end
test "it is possible to add a path that should be autoloaded only once" do
- @root.add "app", with: "/app"
- @root["app"].autoload_once!
- assert @root["app"].autoload_once?
- assert @root.autoload_once.include?(@root["app"].expanded.first)
+ File.stub(:exist?, true) do
+ @root.add "app", with: "/app"
+ @root["app"].autoload_once!
+ assert @root["app"].autoload_once?
+ assert @root.autoload_once.include?(@root["app"].expanded.first)
+ end
end
test "it is possible to remove a path that should be autoloaded only once" do
@@ -110,37 +111,47 @@ class PathsTest < ActiveSupport::TestCase
end
test "it is possible to add a path without assignment and specify it should be loaded only once" do
- @root.add "app", with: "/app", autoload_once: true
- assert @root["app"].autoload_once?
- assert @root.autoload_once.include?("/app")
+ File.stub(:exist?, true) do
+ @root.add "app", with: "/app", autoload_once: true
+ assert @root["app"].autoload_once?
+ assert @root.autoload_once.include?("/app")
+ end
end
test "it is possible to add multiple paths without assignment and specify it should be loaded only once" do
- @root.add "app", with: ["/app", "/app2"], autoload_once: true
- assert @root["app"].autoload_once?
- assert @root.autoload_once.include?("/app")
- assert @root.autoload_once.include?("/app2")
+ File.stub(:exist?, true) do
+ @root.add "app", with: ["/app", "/app2"], autoload_once: true
+ assert @root["app"].autoload_once?
+ assert @root.autoload_once.include?("/app")
+ assert @root.autoload_once.include?("/app2")
+ end
end
test "making a path autoload_once more than once only includes it once in @root.load_once" do
- @root["app"] = "/app"
- @root["app"].autoload_once!
- @root["app"].autoload_once!
- assert_equal 1, @root.autoload_once.select {|p| p == @root["app"].expanded.first }.size
+ File.stub(:exist?, true) do
+ @root["app"] = "/app"
+ @root["app"].autoload_once!
+ @root["app"].autoload_once!
+ assert_equal 1, @root.autoload_once.select {|p| p == @root["app"].expanded.first }.size
+ end
end
test "paths added to a load_once path should be added to the autoload_once collection" do
- @root["app"] = "/app"
- @root["app"].autoload_once!
- @root["app"] << "/app2"
- assert_equal 2, @root.autoload_once.size
+ File.stub(:exist?, true) do
+ @root["app"] = "/app"
+ @root["app"].autoload_once!
+ @root["app"] << "/app2"
+ assert_equal 2, @root.autoload_once.size
+ end
end
test "it is possible to mark a path as eager loaded" do
- @root["app"] = "/app"
- @root["app"].eager_load!
- assert @root["app"].eager_load?
- assert @root.eager_load.include?(@root["app"].to_a.first)
+ File.stub(:exist?, true) do
+ @root["app"] = "/app"
+ @root["app"].eager_load!
+ assert @root["app"].eager_load?
+ assert @root.eager_load.include?(@root["app"].to_a.first)
+ end
end
test "it is possible to skip a path from eager loading" do
@@ -154,38 +165,48 @@ class PathsTest < ActiveSupport::TestCase
end
test "it is possible to add a path without assignment and mark it as eager" do
- @root.add "app", with: "/app", eager_load: true
- assert @root["app"].eager_load?
- assert @root.eager_load.include?("/app")
+ File.stub(:exist?, true) do
+ @root.add "app", with: "/app", eager_load: true
+ assert @root["app"].eager_load?
+ assert @root.eager_load.include?("/app")
+ end
end
test "it is possible to add multiple paths without assignment and mark them as eager" do
- @root.add "app", with: ["/app", "/app2"], eager_load: true
- assert @root["app"].eager_load?
- assert @root.eager_load.include?("/app")
- assert @root.eager_load.include?("/app2")
+ File.stub(:exist?, true) do
+ @root.add "app", with: ["/app", "/app2"], eager_load: true
+ assert @root["app"].eager_load?
+ assert @root.eager_load.include?("/app")
+ assert @root.eager_load.include?("/app2")
+ end
end
test "it is possible to create a path without assignment and mark it both as eager and load once" do
- @root.add "app", with: "/app", eager_load: true, autoload_once: true
- assert @root["app"].eager_load?
- assert @root["app"].autoload_once?
- assert @root.eager_load.include?("/app")
- assert @root.autoload_once.include?("/app")
+ File.stub(:exist?, true) do
+ @root.add "app", with: "/app", eager_load: true, autoload_once: true
+ assert @root["app"].eager_load?
+ assert @root["app"].autoload_once?
+ assert @root.eager_load.include?("/app")
+ assert @root.autoload_once.include?("/app")
+ end
end
test "making a path eager more than once only includes it once in @root.eager_paths" do
- @root["app"] = "/app"
- @root["app"].eager_load!
- @root["app"].eager_load!
- assert_equal 1, @root.eager_load.select {|p| p == @root["app"].expanded.first }.size
+ File.stub(:exist?, true) do
+ @root["app"] = "/app"
+ @root["app"].eager_load!
+ @root["app"].eager_load!
+ assert_equal 1, @root.eager_load.select {|p| p == @root["app"].expanded.first }.size
+ end
end
test "paths added to an eager_load path should be added to the eager_load collection" do
- @root["app"] = "/app"
- @root["app"].eager_load!
- @root["app"] << "/app2"
- assert_equal 2, @root.eager_load.size
+ File.stub(:exist?, true) do
+ @root["app"] = "/app"
+ @root["app"].eager_load!
+ @root["app"] << "/app2"
+ assert_equal 2, @root.eager_load.size
+ end
end
test "it should be possible to add a path's default glob" do
@@ -207,28 +228,36 @@ class PathsTest < ActiveSupport::TestCase
end
test "a path can be added to the load path" do
- @root["app"] = "app"
- @root["app"].load_path!
- @root["app/models"] = "app/models"
- assert_equal ["/foo/bar/app"], @root.load_paths
+ File.stub(:exist?, true) do
+ @root["app"] = "app"
+ @root["app"].load_path!
+ @root["app/models"] = "app/models"
+ assert_equal ["/foo/bar/app"], @root.load_paths
+ end
end
test "a path can be added to the load path on creation" do
- @root.add "app", with: "/app", load_path: true
- assert @root["app"].load_path?
- assert_equal ["/app"], @root.load_paths
+ File.stub(:exist?, true) do
+ @root.add "app", with: "/app", load_path: true
+ assert @root["app"].load_path?
+ assert_equal ["/app"], @root.load_paths
+ end
end
test "a path can be marked as autoload path" do
- @root["app"] = "app"
- @root["app"].autoload!
- @root["app/models"] = "app/models"
- assert_equal ["/foo/bar/app"], @root.autoload_paths
+ File.stub(:exist?, true) do
+ @root["app"] = "app"
+ @root["app"].autoload!
+ @root["app/models"] = "app/models"
+ assert_equal ["/foo/bar/app"], @root.autoload_paths
+ end
end
test "a path can be marked as autoload on creation" do
- @root.add "app", with: "/app", autoload: true
- assert @root["app"].autoload?
- assert_equal ["/app"], @root.autoload_paths
+ File.stub(:exist?, true) do
+ @root.add "app", with: "/app", autoload: true
+ assert @root["app"].autoload?
+ assert_equal ["/app"], @root.autoload_paths
+ end
end
end
diff --git a/railties/test/test_unit/runner_test.rb b/railties/test/test_unit/runner_test.rb
deleted file mode 100644
index 9ea8b2c114..0000000000
--- a/railties/test/test_unit/runner_test.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-require 'abstract_unit'
-require 'env_helpers'
-require 'rails/test_unit/runner'
-
-class TestUnitTestRunnerTest < ActiveSupport::TestCase
- include EnvHelpers
-
- setup do
- @options = Rails::TestRunner::Options
- end
-
- test "shows the filtered backtrace by default" do
- options = @options.parse([])
- assert_not options[:backtrace]
- end
-
- test "has --backtrace (-b) option to show the full backtrace" do
- options = @options.parse(["-b"])
- assert options[:backtrace]
-
- options = @options.parse(["--backtrace"])
- assert options[:backtrace]
- end
-
- test "show full backtrace using BACKTRACE environment variable" do
- switch_env "BACKTRACE", "true" do
- options = @options.parse([])
- assert options[:backtrace]
- end
- end
-
- test "tests run in the test environment by default" do
- options = @options.parse([])
- assert_equal "test", options[:environment]
- end
-
- test "can run in a specific environment" do
- options = @options.parse(["-e development"])
- assert_equal "development", options[:environment]
- end
-
- test "parse the filename and line" do
- file = "test/test_unit/runner_test.rb"
- absolute_file = File.expand_path __FILE__
- options = @options.parse(["#{file}:20"])
- assert_equal absolute_file, options[:filename]
- assert_equal 20, options[:line]
-
- options = @options.parse(["#{file}:"])
- assert_equal [absolute_file], options[:patterns]
- assert_nil options[:line]
-
- options = @options.parse([file])
- assert_equal [absolute_file], options[:patterns]
- assert_nil options[:line]
- end
-
- test "find_method on same file" do
- options = @options.parse(["#{__FILE__}:#{__LINE__}"])
- runner = Rails::TestRunner.new(options)
- assert_equal "test_find_method_on_same_file", runner.find_method
- end
-
- test "find_method on a different file" do
- options = @options.parse(["foobar.rb:#{__LINE__}"])
- runner = Rails::TestRunner.new(options)
- assert_nil runner.find_method
- end
-
- test "run all tests in a directory" do
- options = @options.parse([__dir__])
-
- assert_equal ["#{__dir__}/**/*_test.rb"], options[:patterns]
- assert_nil options[:filename]
- assert_nil options[:line]
- end
-
- test "run multiple folders" do
- application_dir = File.expand_path("#{__dir__}/../application")
-
- options = @options.parse([__dir__, application_dir])
-
- assert_equal ["#{__dir__}/**/*_test.rb", "#{application_dir}/**/*_test.rb"], options[:patterns]
- assert_nil options[:filename]
- assert_nil options[:line]
-
- runner = Rails::TestRunner.new(options)
- assert runner.test_files.size > 0
- end
-
- test "run multiple files and run one file by line" do
- line = __LINE__
- absolute_file = File.expand_path(__FILE__)
- options = @options.parse([__dir__, "#{__FILE__}:#{line}"])
-
- assert_equal ["#{__dir__}/**/*_test.rb"], options[:patterns]
- assert_equal absolute_file, options[:filename]
- assert_equal line, options[:line]
-
- runner = Rails::TestRunner.new(options)
- assert_equal [absolute_file], runner.test_files, 'Only returns the file that running by line'
- end
-
- test "running multiple files passing line number" do
- line = __LINE__
- options = @options.parse(["foobar.rb:8", "#{__FILE__}:#{line}"])
-
- assert_equal File.expand_path(__FILE__), options[:filename], 'Returns the last file'
- assert_equal line, options[:line]
- end
-end
diff --git a/tools/profile b/tools/profile
index eb7fc7792b..191e73b3dd 100755
--- a/tools/profile
+++ b/tools/profile
@@ -117,9 +117,9 @@ rescue LoadError
def camelize(uppercase_first_letter = true)
string = self
if uppercase_first_letter
- string = string.sub(/^[a-z\d]*/) { $&.capitalize }
+ string = string.sub(/^[a-z\d]*/) { |match| match.capitalize }
else
- string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase }
+ string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
end
string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
end