aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile11
-rw-r--r--actionmailer/CHANGELOG.md4
-rw-r--r--actionmailer/README.rdoc16
-rw-r--r--actionmailer/Rakefile4
-rw-r--r--actionmailer/lib/action_mailer/base.rb84
-rw-r--r--actionmailer/lib/action_mailer/collector.rb2
-rw-r--r--actionmailer/lib/action_mailer/delivery_methods.rb22
-rw-r--r--actionmailer/lib/action_mailer/log_subscriber.rb6
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb1
-rw-r--r--actionmailer/lib/rails/generators/mailer/USAGE2
-rw-r--r--actionmailer/lib/rails/generators/mailer/mailer_generator.rb2
-rw-r--r--actionmailer/test/asset_host_test.rb6
-rw-r--r--actionmailer/test/base_test.rb64
-rw-r--r--actionmailer/test/delivery_methods_test.rb52
-rw-r--r--actionmailer/test/i18n_with_controller_test.rb8
-rw-r--r--actionmailer/test/mail_helper_test.rb16
-rw-r--r--actionmailer/test/mail_layout_test.rb20
-rw-r--r--actionmailer/test/mailers/base_mailer.rb40
-rw-r--r--actionmailer/test/mailers/proc_mailer.rb4
-rw-r--r--actionmailer/test/test_helper_test.rb6
-rw-r--r--actionmailer/test/url_test.rb8
-rw-r--r--actionpack/CHANGELOG.md81
-rw-r--r--actionpack/actionpack.gemspec1
-rw-r--r--actionpack/lib/abstract_controller/asset_paths.rb2
-rw-r--r--actionpack/lib/abstract_controller/base.rb6
-rw-r--r--actionpack/lib/action_controller/base.rb18
-rw-r--r--actionpack/lib/action_controller/caching.rb24
-rw-r--r--actionpack/lib/action_controller/caching/actions.rb189
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb202
-rw-r--r--actionpack/lib/action_controller/log_subscriber.rb17
-rw-r--r--actionpack/lib/action_controller/metal/hide_actions.rb10
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb33
-rw-r--r--actionpack/lib/action_controller/railtie.rb2
-rw-r--r--actionpack/lib/action_controller/test_case.rb51
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb29
-rw-r--r--actionpack/lib/action_dispatch/http/headers.rb43
-rw-r--r--actionpack/lib/action_dispatch/http/parameter_filter.rb90
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb8
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb8
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb2
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb7
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb4
-rw-r--r--actionpack/lib/action_view.rb1
-rw-r--r--actionpack/lib/action_view/asset_paths.rb143
-rw-r--r--actionpack/lib/action_view/digestor.rb40
-rw-r--r--actionpack/lib/action_view/helpers.rb6
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb358
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb145
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb93
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb195
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb151
-rw-r--r--actionpack/lib/action_view/helpers/asset_url_helper.rb355
-rw-r--r--actionpack/lib/action_view/helpers/cache_helper.rb50
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb101
-rw-r--r--actionpack/lib/action_view/helpers/tags/check_box.rb8
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb105
-rw-r--r--actionpack/lib/action_view/log_subscriber.rb7
-rw-r--r--actionpack/lib/action_view/railtie.rb22
-rw-r--r--actionpack/lib/action_view/test_case.rb43
-rw-r--r--actionpack/lib/action_view/vendor/html-scanner/html/selector.rb2
-rw-r--r--actionpack/test/active_record_unit.rb2
-rw-r--r--actionpack/test/controller/caching_test.rb750
-rw-r--r--actionpack/test/controller/log_subscriber_test.rb18
-rw-r--r--actionpack/test/controller/parameters/multi_parameter_attributes_test.rb38
-rw-r--r--actionpack/test/controller/parameters/nested_parameters_test.rb6
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb9
-rw-r--r--actionpack/test/controller/render_test.rb11
-rw-r--r--actionpack/test/controller/show_exceptions_test.rb16
-rw-r--r--actionpack/test/dispatch/header_test.rb20
-rw-r--r--actionpack/test/dispatch/routing_test.rb20
-rw-r--r--actionpack/test/dispatch/session/cache_store_test.rb1
-rw-r--r--actionpack/test/fixtures/digestor/level/below/_header.html.erb0
-rw-r--r--actionpack/test/fixtures/digestor/level/below/index.html.erb1
-rw-r--r--actionpack/test/fixtures/test/render_two_partials.html.erb2
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb1029
-rw-r--r--actionpack/test/template/date_helper_i18n_test.rb29
-rw-r--r--actionpack/test/template/date_helper_test.rb16
-rw-r--r--actionpack/test/template/digestor_test.rb20
-rw-r--r--actionpack/test/template/form_helper_test.rb6
-rw-r--r--actionpack/test/template/html-scanner/sanitizer_test.rb2
-rw-r--r--actionpack/test/template/sanitize_helper_test.rb6
-rw-r--r--actionpack/test/template/test_case_test.rb8
-rw-r--r--actionpack/test/template/url_helper_test.rb338
-rw-r--r--activemodel/CHANGELOG.md2
-rw-r--r--activemodel/README.rdoc2
-rw-r--r--activemodel/lib/active_model/validations.rb15
-rw-r--r--activemodel/test/cases/secure_password_test.rb2
-rw-r--r--activerecord/CHANGELOG.md116
-rw-r--r--activerecord/lib/active_record/associations.rb18
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb24
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb5
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb34
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb51
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb10
-rw-r--r--activerecord/lib/active_record/counter_cache.rb4
-rw-r--r--activerecord/lib/active_record/fixture_set/file.rb (renamed from activerecord/lib/active_record/fixtures/file.rb)2
-rw-r--r--activerecord/lib/active_record/fixtures.rb44
-rw-r--r--activerecord/lib/active_record/locale/en.yml6
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb10
-rw-r--r--activerecord/lib/active_record/migration.rb33
-rw-r--r--activerecord/lib/active_record/persistence.rb4
-rw-r--r--activerecord/lib/active_record/querying.rb1
-rw-r--r--activerecord/lib/active_record/railtie.rb3
-rw-r--r--activerecord/lib/active_record/railties/databases.rake8
-rw-r--r--activerecord/lib/active_record/relation.rb53
-rw-r--r--activerecord/lib/active_record/relation/batches.rb2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb4
-rw-r--r--activerecord/lib/active_record/relation/merger.rb22
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb71
-rw-r--r--activerecord/lib/active_record/sanitization.rb17
-rw-r--r--activerecord/lib/active_record/timestamp.rb3
-rw-r--r--activerecord/lib/active_record/validations/presence.rb6
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb3
-rw-r--r--activerecord/test/cases/adapters/mysql/reserved_word_test.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql2/reserved_word_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/quoting_test.rb13
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb4
-rw-r--r--activerecord/test/cases/adapters/sqlite3/copy_table_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb7
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb8
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb4
-rw-r--r--activerecord/test/cases/autosave_association_test.rb3
-rw-r--r--activerecord/test/cases/base_test.rb337
-rw-r--r--activerecord/test/cases/calculations_test.rb23
-rw-r--r--activerecord/test/cases/counter_cache_test.rb15
-rw-r--r--activerecord/test/cases/defaults_test.rb112
-rw-r--r--activerecord/test/cases/dirty_test.rb44
-rw-r--r--activerecord/test/cases/dup_test.rb14
-rw-r--r--activerecord/test/cases/fixture_set/file_test.rb (renamed from activerecord/test/cases/fixtures/file_test.rb)2
-rw-r--r--activerecord/test/cases/fixtures_test.rb46
-rw-r--r--activerecord/test/cases/helper.rb2
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb2
-rw-r--r--activerecord/test/cases/migration_test.rb6
-rw-r--r--activerecord/test/cases/multiparameter_attributes_test.rb350
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb2
-rw-r--r--activerecord/test/cases/query_cache_test.rb2
-rw-r--r--activerecord/test/cases/reflection_test.rb1
-rw-r--r--activerecord/test/cases/relations_test.rb50
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb6
-rw-r--r--activerecord/test/cases/store_test.rb2
-rw-r--r--activerecord/test/cases/validations/i18n_generate_message_validation_test.rb7
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb2
-rw-r--r--activerecord/test/cases/validations/presence_validation_test.rb7
-rw-r--r--activerecord/test/fixtures/mateys.yml4
-rw-r--r--activerecord/test/fixtures/parrots_pirates.yml8
-rw-r--r--activerecord/test/fixtures/peoples_treasures.yml4
-rw-r--r--activerecord/test/models/admin/user.rb2
-rw-r--r--activerecord/test/models/subscription.rb2
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb9
-rw-r--r--activerecord/test/schema/schema.rb1
-rw-r--r--activerecord/test/support/connection.rb2
-rw-r--r--activesupport/CHANGELOG.md15
-rw-r--r--activesupport/activesupport.gemspec2
-rw-r--r--activesupport/lib/active_support.rb1
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb5
-rw-r--r--activesupport/lib/active_support/cache.rb148
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb4
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/hash/slice.rb6
-rw-r--r--activesupport/lib/active_support/deprecation.rb21
-rw-r--r--activesupport/lib/active_support/descendants_tracker.rb53
-rw-r--r--activesupport/lib/active_support/key_generator.rb23
-rw-r--r--activesupport/lib/active_support/queueing.rb30
-rw-r--r--activesupport/lib/active_support/testing/performance/ruby.rb2
-rw-r--r--activesupport/test/caching_test.rb107
-rw-r--r--activesupport/test/callbacks_test.rb2
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb26
-rw-r--r--activesupport/test/descendants_tracker_test_cases.rb20
-rw-r--r--activesupport/test/descendants_tracker_with_autoloading_test.rb10
-rw-r--r--activesupport/test/key_generator_test.rb32
-rw-r--r--activesupport/test/ordered_hash_test.rb1
-rw-r--r--activesupport/test/queueing/container_test.rb28
-rw-r--r--activesupport/test/queueing/test_queue_test.rb44
-rw-r--r--activesupport/test/testing/performance_test.rb15
-rw-r--r--guides/CHANGELOG.md5
-rw-r--r--guides/assets/javascripts/guides.js50
-rw-r--r--guides/assets/javascripts/jquery.min.js4
-rwxr-xr-xguides/assets/javascripts/responsive-tables.js43
-rw-r--r--guides/assets/stylesheets/main.css667
-rwxr-xr-xguides/assets/stylesheets/responsive-tables.css50
-rw-r--r--guides/code/getting_started/app/assets/javascripts/application.js2
-rw-r--r--guides/rails_guides/helpers.rb10
-rw-r--r--guides/source/4_0_release_notes.md10
-rw-r--r--guides/source/action_controller_overview.md4
-rw-r--r--guides/source/action_mailer_basics.md4
-rw-r--r--guides/source/active_record_querying.md58
-rw-r--r--guides/source/active_record_validations_callbacks.md3
-rw-r--r--guides/source/active_support_core_extensions.md31
-rw-r--r--guides/source/association_basics.md23
-rw-r--r--guides/source/caching_with_rails.md15
-rw-r--r--guides/source/command_line.md22
-rw-r--r--guides/source/configuring.md34
-rw-r--r--guides/source/engines.md65
-rw-r--r--guides/source/generators.md12
-rw-r--r--guides/source/getting_started.md67
-rw-r--r--guides/source/i18n.md4
-rw-r--r--guides/source/initialization.md4
-rw-r--r--guides/source/layout.html.erb52
-rw-r--r--guides/source/migrations.md37
-rw-r--r--guides/source/rails_on_rack.md4
-rw-r--r--guides/source/routing.md106
-rw-r--r--guides/source/security.md35
-rw-r--r--guides/source/testing.md30
-rw-r--r--guides/source/upgrading_ruby_on_rails.md15
-rw-r--r--rails.gemspec2
-rw-r--r--railties/CHANGELOG.md20
-rw-r--r--railties/lib/rails.rb4
-rw-r--r--railties/lib/rails/all.rb2
-rw-r--r--railties/lib/rails/application.rb36
-rw-r--r--railties/lib/rails/application/bootstrap.rb14
-rw-r--r--railties/lib/rails/application/configuration.rb18
-rw-r--r--railties/lib/rails/application/finisher.rb9
-rw-r--r--railties/lib/rails/application/routes_reloader.rb2
-rw-r--r--railties/lib/rails/commands.rb55
-rw-r--r--railties/lib/rails/commands/application.rb2
-rw-r--r--railties/lib/rails/commands/destroy.rb2
-rw-r--r--railties/lib/rails/commands/generate.rb2
-rw-r--r--railties/lib/rails/commands/runner.rb2
-rw-r--r--railties/lib/rails/commands/server.rb14
-rw-r--r--railties/lib/rails/commands/update.rb2
-rw-r--r--railties/lib/rails/engine.rb16
-rw-r--r--railties/lib/rails/engine/configuration.rb30
-rw-r--r--railties/lib/rails/generators.rb58
-rw-r--r--railties/lib/rails/generators/actions.rb44
-rw-r--r--railties/lib/rails/generators/app_base.rb15
-rw-r--r--railties/lib/rails/generators/base.rb18
-rw-r--r--railties/lib/rails/generators/css/assets/assets_generator.rb6
-rw-r--r--railties/lib/rails/generators/css/scaffold/scaffold_generator.rb6
-rw-r--r--railties/lib/rails/generators/erb.rb4
-rw-r--r--railties/lib/rails/generators/erb/controller/controller_generator.rb8
-rw-r--r--railties/lib/rails/generators/erb/mailer/mailer_generator.rb6
-rw-r--r--railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb8
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb10
-rw-r--r--railties/lib/rails/generators/js/assets/assets_generator.rb6
-rw-r--r--railties/lib/rails/generators/named_base.rb8
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb18
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb21
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt13
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb7
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/routes.rb26
-rw-r--r--railties/lib/rails/generators/rails/assets/assets_generator.rb10
-rw-r--r--railties/lib/rails/generators/rails/controller/USAGE8
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb6
-rw-r--r--railties/lib/rails/generators/rails/generator/generator_generator.rb8
-rw-r--r--railties/lib/rails/generators/rails/helper/USAGE2
-rw-r--r--railties/lib/rails/generators/rails/helper/helper_generator.rb4
-rw-r--r--railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb4
-rw-r--r--railties/lib/rails/generators/rails/migration/migration_generator.rb6
-rw-r--r--railties/lib/rails/generators/rails/model/USAGE4
-rw-r--r--railties/lib/rails/generators/rails/model/model_generator.rb6
-rw-r--r--railties/lib/rails/generators/rails/observer/USAGE2
-rw-r--r--railties/lib/rails/generators/rails/observer/observer_generator.rb4
-rw-r--r--railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb4
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb41
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb2
-rw-r--r--railties/lib/rails/generators/rails/resource/resource_generator.rb10
-rw-r--r--railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb47
-rw-r--r--railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb8
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/USAGE8
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb14
-rw-r--r--railties/lib/rails/generators/rails/task/task_generator.rb4
-rw-r--r--railties/lib/rails/generators/rails/task/templates/task.rb2
-rw-r--r--railties/lib/rails/generators/resource_helpers.rb6
-rw-r--r--railties/lib/rails/generators/test_case.rb29
-rw-r--r--railties/lib/rails/generators/test_unit.rb6
-rw-r--r--railties/lib/rails/generators/test_unit/controller/controller_generator.rb12
-rw-r--r--railties/lib/rails/generators/test_unit/helper/helper_generator.rb10
-rw-r--r--railties/lib/rails/generators/test_unit/integration/integration_generator.rb8
-rw-r--r--railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb12
-rw-r--r--railties/lib/rails/generators/test_unit/model/model_generator.rb14
-rw-r--r--railties/lib/rails/generators/test_unit/observer/observer_generator.rb10
-rw-r--r--railties/lib/rails/generators/test_unit/performance/performance_generator.rb8
-rw-r--r--railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb4
-rw-r--r--railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb8
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb12
-rw-r--r--railties/lib/rails/info_controller.rb2
-rw-r--r--railties/lib/rails/paths.rb7
-rw-r--r--railties/lib/rails/rack/logger.rb9
-rw-r--r--railties/lib/rails/railtie.rb2
-rw-r--r--railties/lib/rails/railtie/configurable.rb2
-rw-r--r--railties/lib/rails/railtie/configuration.rb8
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb14
-rw-r--r--railties/lib/rails/tasks/annotations.rake2
-rw-r--r--railties/lib/rails/tasks/framework.rake10
-rw-r--r--railties/lib/rails/tasks/middleware.rake2
-rw-r--r--railties/lib/rails/tasks/misc.rake4
-rw-r--r--railties/lib/rails/tasks/routes.rake2
-rw-r--r--railties/lib/rails/tasks/statistics.rake8
-rw-r--r--railties/lib/rails/tasks/tmp.rake4
-rw-r--r--railties/lib/rails/test_help.rb8
-rw-r--r--railties/lib/rails/test_unit/railtie.rb4
-rw-r--r--railties/lib/rails/test_unit/testing.rake56
-rw-r--r--railties/test/application/asset_debugging_test.rb11
-rw-r--r--railties/test/application/assets_test.rb201
-rw-r--r--railties/test/application/configuration_test.rb109
-rw-r--r--railties/test/application/generators_test.rb30
-rw-r--r--railties/test/application/initializers/frameworks_test.rb7
-rw-r--r--railties/test/application/initializers/i18n_test.rb14
-rw-r--r--railties/test/application/initializers/notifications_test.rb2
-rw-r--r--railties/test/application/loading_test.rb28
-rw-r--r--railties/test/application/middleware/cache_test.rb36
-rw-r--r--railties/test/application/middleware/session_test.rb2
-rw-r--r--railties/test/application/middleware_test.rb18
-rw-r--r--railties/test/application/queue_test.rb79
-rw-r--r--railties/test/application/rake/notes_test.rb4
-rw-r--r--railties/test/application/rake_test.rb22
-rw-r--r--railties/test/application/routing_test.rb28
-rw-r--r--railties/test/application/url_generation_test.rb7
-rw-r--r--railties/test/commands/console_test.rb22
-rw-r--r--railties/test/fixtures/lib/plugin_builders/spec_builder.rb2
-rw-r--r--railties/test/generators/actions_test.rb36
-rw-r--r--railties/test/generators/app_generator_test.rb17
-rw-r--r--railties/test/generators/controller_generator_test.rb8
-rw-r--r--railties/test/generators/helper_generator_test.rb2
-rw-r--r--railties/test/generators/mailer_generator_test.rb2
-rw-r--r--railties/test/generators/model_generator_test.rb12
-rw-r--r--railties/test/generators/namespaced_generators_test.rb116
-rw-r--r--railties/test/generators/observer_generator_test.rb2
-rw-r--r--railties/test/generators/orm_test.rb4
-rw-r--r--railties/test/generators/plugin_new_generator_test.rb2
-rw-r--r--railties/test/generators/resource_generator_test.rb12
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb8
-rw-r--r--railties/test/generators/scaffold_generator_test.rb30
-rw-r--r--railties/test/generators/shared_generator_tests.rb14
-rw-r--r--railties/test/generators_test.rb8
-rw-r--r--railties/test/initializable_test.rb18
-rw-r--r--railties/test/isolation/abstract_unit.rb8
-rw-r--r--railties/test/paths_test.rb28
-rw-r--r--railties/test/rails_info_controller_test.rb6
-rw-r--r--railties/test/railties/engine_test.rb42
-rw-r--r--railties/test/railties/generators_test.rb2
-rw-r--r--railties/test/railties/mounted_engine_test.rb38
-rwxr-xr-xtools/profile2
355 files changed, 5251 insertions, 6049 deletions
diff --git a/Gemfile b/Gemfile
index d149ec6129..6e0f43c526 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,8 +6,11 @@ gem 'arel', github: 'rails/arel', branch: 'master'
gem 'mocha', '>= 0.11.2', :require => false
gem 'rack-test', github: 'brynary/rack-test'
+gem 'rack-cache', '~> 1.2'
gem 'bcrypt-ruby', '~> 3.0.0'
-gem 'jquery-rails'
+gem 'jquery-rails', '~> 2.1.4', github: 'rails/jquery-rails'
+gem 'turbolinks'
+gem 'coffee-rails', github: 'rails/coffee-rails'
gem 'journey', github: 'rails/journey', branch: 'master'
@@ -25,7 +28,7 @@ group :doc do
# for some weeks unapplied. As a temporary solution
# this is our own fork with the fix.
gem 'sdoc', github: 'fxn/sdoc'
- gem 'redcarpet', '~> 2.1.1'
+ gem 'redcarpet', '~> 2.1.1', :platforms => :ruby
gem 'w3c_validators'
end
@@ -38,8 +41,8 @@ instance_eval File.read local_gemfile if File.exists? local_gemfile
platforms :mri do
group :test do
- gem 'ruby-prof', '~> 0.11.2'
- gem 'debugger' if !ENV['TRAVIS'] && RUBY_VERSION < "2.0"
+ gem 'ruby-prof', '~> 0.11.2' if RUBY_VERSION < '2.0'
+ gem 'debugger' if !ENV['TRAVIS'] && RUBY_VERSION < '2.0' && RUBY_PATCHLEVEL < 286
end
end
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index e29890f2d8..68b34805ff 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -27,4 +27,8 @@
* Asynchronously send messages via the Rails Queue *Brian Cardarella*
+* Allow callbacks to be defined in mailers similar to `ActionController::Base`. You can configure default
+ settings, headers, attachments, delivery settings or change delivery using
+ `before_filter`, `after_filter` etc. *Justin S. Leitgeb*
+
Please check [3-2-stable](https://github.com/rails/rails/blob/3-2-stable/actionmailer/CHANGELOG.md) for previous changes.
diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc
index cf10bfffdb..59c33b7940 100644
--- a/actionmailer/README.rdoc
+++ b/actionmailer/README.rdoc
@@ -83,7 +83,7 @@ Note that every value you set with this method will get over written if you use
Example:
class AuthenticationMailer < ActionMailer::Base
- default :from => "awesome@application.com", :subject => Proc.new { "E-mail was generated at #{Time.now}" }
+ default from: "awesome@application.com", subject: Proc.new { "E-mail was generated at #{Time.now}" }
.....
end
@@ -100,13 +100,13 @@ Example:
def receive(email)
page = Page.find_by_address(email.to.first)
page.emails.create(
- :subject => email.subject, :body => email.body
+ subject: email.subject, body: email.body
)
if email.has_attachments?
email.attachments.each do |attachment|
page.attachments.create({
- :file => attachment, :description => email.subject
+ file: attachment, description: email.subject
})
end
end
@@ -127,11 +127,11 @@ a limited number of email.
The Base class has the full list of configuration options. Here's an example:
ActionMailer::Base.smtp_settings = {
- :address => 'smtp.yourserver.com', # default: localhost
- :port => '25', # default: 25
- :user_name => 'user',
- :password => 'pass',
- :authentication => :plain # :plain, :login or :cram_md5
+ address: 'smtp.yourserver.com', # default: localhost
+ port: '25', # default: 25
+ user_name: 'user',
+ password: 'pass',
+ authentication: :plain # :plain, :login or :cram_md5
}
diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile
index c1c4171cdf..45c238d302 100644
--- a/actionmailer/Rakefile
+++ b/actionmailer/Rakefile
@@ -3,7 +3,7 @@ require 'rake/packagetask'
require 'rubygems/package_task'
desc "Default Task"
-task :default => [ :test ]
+task default: [ :test ]
# Run the unit tests
Rake::TestTask.new { |t|
@@ -29,7 +29,7 @@ Gem::PackageTask.new(spec) do |p|
end
desc "Release to gemcutter"
-task :release => :package do
+task release: :package do
require 'rake/gemcutter'
Rake::Gemcutter::Tasks.new(spec).define
Rake::Task['gem:push'].invoke
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index bd33f81f1e..fee4a64248 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -23,13 +23,13 @@ module ActionMailer
# Examples:
#
# class Notifier < ActionMailer::Base
- # default :from => 'no-reply@example.com',
- # :return_path => 'system@example.com'
+ # default from: 'no-reply@example.com',
+ # return_path: 'system@example.com'
#
# def welcome(recipient)
# @account = recipient
- # mail(:to => recipient.email_address_with_name,
- # :bcc => ["bcc@example.com", "Order Watcher <watcher@example.com>"])
+ # mail(to: recipient.email_address_with_name,
+ # bcc: ["bcc@example.com", "Order Watcher <watcher@example.com>"])
# end
# end
#
@@ -62,21 +62,21 @@ module ActionMailer
#
# If you want to explicitly render only certain templates, pass a block:
#
- # mail(:to => user.email) do |format|
+ # mail(to: user.email) do |format|
# format.text
# format.html
# end
#
# The block syntax is also useful in providing information specific to a part:
#
- # mail(:to => user.email) do |format|
+ # mail(to: user.email) do |format|
# format.text(:content_transfer_encoding => "base64")
# format.html
# end
#
# Or even to render a special view:
#
- # mail(:to => user.email) do |format|
+ # mail(to: user.email) do |format|
# format.text
# format.html { render "some_other_template" }
# end
@@ -100,12 +100,12 @@ module ActionMailer
# You can even use Action Pack helpers in these views. For example:
#
# You got a new note!
- # <%= truncate(@note.body, :length => 25) %>
+ # <%= truncate(@note.body, length: 25) %>
#
# If you need to access the subject, from or the recipients in the view, you can do that through message object:
#
# You got a new note from <%= message.from %>!
- # <%= truncate(@note.body, :length => 25) %>
+ # <%= truncate(@note.body, length: 25) %>
#
#
# = Generating URLs
@@ -116,11 +116,11 @@ module ActionMailer
#
# When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
#
- # <%= url_for(:host => "example.com", :controller => "welcome", :action => "greeting") %>
+ # <%= url_for(host: "example.com", controller: "welcome", action: "greeting") %>
#
# When using named routes you only need to supply the <tt>:host</tt>:
#
- # <%= users_url(:host => "example.com") %>
+ # <%= users_url(host: "example.com") %>
#
# You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
# <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
@@ -176,7 +176,7 @@ module ActionMailer
# class ApplicationMailer < ActionMailer::Base
# def welcome(recipient)
# attachments['free_book.pdf'] = File.read('path/to/file.pdf')
- # mail(:to => recipient, :subject => "New account information")
+ # mail(to: recipient, subject: "New account information")
# end
# end
#
@@ -192,7 +192,7 @@ module ActionMailer
# class ApplicationMailer < ActionMailer::Base
# def welcome(recipient)
# attachments['free_book.pdf'] = File.read('path/to/file.pdf')
- # mail(:to => recipient, :subject => "New account information", :body => "")
+ # mail(to: recipient, subject: "New account information", body: "")
# end
# end
#
@@ -204,7 +204,7 @@ module ActionMailer
# class ApplicationMailer < ActionMailer::Base
# def welcome(recipient)
# attachments.inline['photo.png'] = File.read('path/to/photo.png')
- # mail(:to => recipient, :subject => "Here is what we look like")
+ # mail(to: recipient, subject: "Here is what we look like")
# end
# end
#
@@ -220,7 +220,7 @@ module ActionMailer
#
# <h1>Please Don't Cringe</h1>
#
- # <%= image_tag attachments['photo.png'].url, :alt => 'Our Photo', :class => 'photo' -%>
+ # <%= image_tag attachments['photo.png'].url, alt: 'Our Photo', class: 'photo' -%>
#
# = Observing and Intercepting Mails
#
@@ -241,7 +241,7 @@ module ActionMailer
# default method inside the class definition:
#
# class Notifier < ActionMailer::Base
- # default :sender => 'system@example.com'
+ # default sender: 'system@example.com'
# end
#
# You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
@@ -260,7 +260,7 @@ module ActionMailer
#
# class Notifier < ActionMailer::Base
# default 'Content-Transfer-Encoding' => '7bit',
- # :content_description => 'This is a description'
+ # content_description: 'This is a description'
# end
#
# Finally, Action Mailer also supports passing <tt>Proc</tt> objects into the default hash, so you
@@ -386,10 +386,10 @@ module ActionMailer
class_attribute :default_params
self.default_params = {
- :mime_version => "1.0",
- :charset => "UTF-8",
- :content_type => "text/plain",
- :parts_order => [ "text/plain", "text/enriched", "text/html" ]
+ mime_version: "1.0",
+ charset: "UTF-8",
+ content_type: "text/plain",
+ parts_order: [ "text/plain", "text/enriched", "text/html" ]
}.freeze
class_attribute :queue
@@ -549,17 +549,17 @@ module ActionMailer
#
# You can also specify overrides if you want by passing a hash instead of a string:
#
- # mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
- # :content => File.read('/path/to/filename.jpg')}
+ # mail.attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
+ # content: File.read('/path/to/filename.jpg')}
#
# If you want to use a different encoding than Base64, you can pass an encoding in,
# but then it is up to you to pass in the content pre-encoded, and don't expect
# Mail to know how to decode this data:
#
# file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
- # mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
- # :encoding => 'SpecialEncoding',
- # :content => file_content }
+ # mail.attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
+ # encoding: 'SpecialEncoding',
+ # content: file_content }
#
# You can also search for specific attachments:
#
@@ -597,9 +597,9 @@ module ActionMailer
# class method:
#
# class Notifier < ActionMailer::Base
- # self.default :from => 'no-reply@test.lindsaar.net',
- # :bcc => 'email_logger@test.lindsaar.net',
- # :reply_to => 'bounces@test.lindsaar.net'
+ # self.default from: 'no-reply@test.lindsaar.net',
+ # bcc: 'email_logger@test.lindsaar.net',
+ # reply_to: 'bounces@test.lindsaar.net'
# end
#
# If you need other headers not listed above, you can either pass them in
@@ -621,10 +621,10 @@ module ActionMailer
# For example:
#
# class Notifier < ActionMailer::Base
- # default :from => 'no-reply@test.lindsaar.net',
+ # default from: 'no-reply@test.lindsaar.net',
#
# def welcome
- # mail(:to => 'mikel@test.lindsaar.net')
+ # mail(to: 'mikel@test.lindsaar.net')
# end
# end
#
@@ -633,22 +633,22 @@ module ActionMailer
#
# However, those can be customized:
#
- # mail(:template_path => 'notifications', :template_name => 'another')
+ # mail(template_path: 'notifications', template_name: 'another')
#
# And now it will look for all templates at "app/views/notifications" with name "another".
#
# If you do pass a block, you can render specific templates of your choice:
#
- # mail(:to => 'mikel@test.lindsaar.net') do |format|
+ # mail(to: 'mikel@test.lindsaar.net') do |format|
# format.text
# format.html
# end
#
# You can even render text directly without using a template:
#
- # mail(:to => 'mikel@test.lindsaar.net') do |format|
- # format.text { render :text => "Hello Mikel!" }
- # format.html { render :text => "<h1>Hello Mikel!</h1>" }
+ # mail(to: 'mikel@test.lindsaar.net') do |format|
+ # format.text { render text: "Hello Mikel!" }
+ # format.html { render text: "<h1>Hello Mikel!</h1>" }
# end
#
# Which will render a <tt>multipart/alternative</tt> email with <tt>text/plain</tt> and
@@ -657,7 +657,7 @@ module ActionMailer
# The block syntax also allows you to customize the part headers if desired:
#
# mail(:to => 'mikel@test.lindsaar.net') do |format|
- # format.text(:content_transfer_encoding => "base64")
+ # format.text(content_transfer_encoding: "base64")
# format.html
# end
#
@@ -730,7 +730,7 @@ module ActionMailer
# humanized version of the <tt>action_name</tt>.
def default_i18n_subject #:nodoc:
mailer_scope = self.class.mailer_name.tr('/', '.')
- I18n.t(:subject, :scope => [mailer_scope, action_name], :default => action_name.humanize)
+ I18n.t(:subject, scope: [mailer_scope, action_name], default: action_name.humanize)
end
def collect_responses_and_parts_order(headers) #:nodoc:
@@ -743,8 +743,8 @@ module ActionMailer
responses = collector.responses
elsif headers[:body]
responses << {
- :body => headers.delete(:body),
- :content_type => self.class.default[:content_type] || "text/plain"
+ body: headers.delete(:body),
+ content_type: self.class.default[:content_type] || "text/plain"
}
else
templates_path = headers.delete(:template_path) || self.class.mailer_name
@@ -754,8 +754,8 @@ module ActionMailer
self.formats = template.formats
responses << {
- :body => render(:template => template),
- :content_type => template.type.to_s
+ body: render(template: template),
+ content_type: template.type.to_s
}
end
end
diff --git a/actionmailer/lib/action_mailer/collector.rb b/actionmailer/lib/action_mailer/collector.rb
index 569a09bd2a..cbb2778fae 100644
--- a/actionmailer/lib/action_mailer/collector.rb
+++ b/actionmailer/lib/action_mailer/collector.rb
@@ -21,7 +21,7 @@ module ActionMailer
alias :all :any
def custom(mime, options={})
- options.reverse_merge!(:content_type => mime.to_s)
+ options.reverse_merge!(content_type: mime.to_s)
@context.formats = [mime.to_sym]
options[:body] = block_given? ? yield : @default_render.call
@responses << options
diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb
index 110f6769e6..caea3d7535 100644
--- a/actionmailer/lib/action_mailer/delivery_methods.rb
+++ b/actionmailer/lib/action_mailer/delivery_methods.rb
@@ -20,27 +20,27 @@ module ActionMailer
self.delivery_method = :smtp
add_delivery_method :smtp, Mail::SMTP,
- :address => "localhost",
- :port => 25,
- :domain => 'localhost.localdomain',
- :user_name => nil,
- :password => nil,
- :authentication => nil,
- :enable_starttls_auto => true
+ address: "localhost",
+ port: 25,
+ domain: 'localhost.localdomain',
+ user_name: nil,
+ password: nil,
+ authentication: nil,
+ enable_starttls_auto: true
add_delivery_method :file, Mail::FileDelivery,
- :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
+ location: defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
add_delivery_method :sendmail, Mail::Sendmail,
- :location => '/usr/sbin/sendmail',
- :arguments => '-i -t'
+ location: '/usr/sbin/sendmail',
+ arguments: '-i -t'
add_delivery_method :test, Mail::TestMailer
end
module ClassMethods
# Provides a list of emails that have been delivered by Mail::TestMailer
- delegate :deliveries, :deliveries=, :to => Mail::TestMailer
+ delegate :deliveries, :deliveries=, to: Mail::TestMailer
# Adds a new delivery method through the given class using the given
# symbol as alias and the default options supplied.
diff --git a/actionmailer/lib/action_mailer/log_subscriber.rb b/actionmailer/lib/action_mailer/log_subscriber.rb
index a6c163832e..3fe64759ac 100644
--- a/actionmailer/lib/action_mailer/log_subscriber.rb
+++ b/actionmailer/lib/action_mailer/log_subscriber.rb
@@ -1,13 +1,15 @@
module ActionMailer
class LogSubscriber < ActiveSupport::LogSubscriber
def deliver(event)
+ return unless logger.info?
recipients = Array(event.payload[:to]).join(', ')
- info("\nSent mail to #{recipients} (%1.fms)" % event.duration)
+ info("\nSent mail to #{recipients} (#{event.duration.round(1)}ms)")
debug(event.payload[:mail])
end
def receive(event)
- info("\nReceived mail (%.1fms)" % event.duration)
+ return unless logger.info?
+ info("\nReceived mail (#{event.duration.round(1)}ms)")
debug(event.payload[:mail])
end
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index abf6ad80cf..59dc26841f 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -22,7 +22,6 @@ module ActionMailer
options.queue ||= app.queue
# make sure readers methods get compiled
- options.asset_path ||= app.config.asset_path
options.asset_host ||= app.config.asset_host
options.relative_url_root ||= app.config.relative_url_root
diff --git a/actionmailer/lib/rails/generators/mailer/USAGE b/actionmailer/lib/rails/generators/mailer/USAGE
index 9f1d6b182e..7470289fd3 100644
--- a/actionmailer/lib/rails/generators/mailer/USAGE
+++ b/actionmailer/lib/rails/generators/mailer/USAGE
@@ -13,6 +13,6 @@ Example:
creates a Notifications mailer class, views, test, and fixtures:
Mailer: app/mailers/notifications.rb
Views: app/views/notifications/signup.erb [...]
- Test: test/functional/notifications_test.rb
+ Test: test/mailers/notifications_test.rb
Fixtures: test/fixtures/notifications/signup [...]
diff --git a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
index dd7fa640c9..d5bf864595 100644
--- a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
+++ b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
@@ -3,7 +3,7 @@ module Rails
class MailerGenerator < NamedBase
source_root File.expand_path("../templates", __FILE__)
- argument :actions, :type => :array, :default => [], :banner => "method method"
+ argument :actions, type: :array, default: [], banner: "method method"
check_class_collision
def create_mailer_file
diff --git a/actionmailer/test/asset_host_test.rb b/actionmailer/test/asset_host_test.rb
index 696a9f1174..32ae76a7c5 100644
--- a/actionmailer/test/asset_host_test.rb
+++ b/actionmailer/test/asset_host_test.rb
@@ -3,9 +3,9 @@ require 'action_controller'
class AssetHostMailer < ActionMailer::Base
def email_with_asset
- mail :to => 'test@localhost',
- :subject => 'testing email containing asset path while asset_host is set',
- :from => 'tester@example.com'
+ mail to: 'test@localhost',
+ subject: 'testing email containing asset path while asset_host is set',
+ from: 'tester@example.com'
end
end
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 17ce8b7072..1cb3ce63fe 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -30,21 +30,21 @@ class BaseTest < ActiveSupport::TestCase
end
test "mail() with from overwrites the class level default" do
- email = BaseMailer.welcome(:from => 'someone@example.com',
- :to => 'another@example.org')
+ email = BaseMailer.welcome(from: 'someone@example.com',
+ to: 'another@example.org')
assert_equal(['someone@example.com'], email.from)
assert_equal(['another@example.org'], email.to)
end
test "mail() with bcc, cc, content_type, charset, mime_version, reply_to and date" do
time = Time.now.beginning_of_day.to_datetime
- email = BaseMailer.welcome(:bcc => 'bcc@test.lindsaar.net',
- :cc => 'cc@test.lindsaar.net',
- :content_type => 'multipart/mixed',
- :charset => 'iso-8559-1',
- :mime_version => '2.0',
- :reply_to => 'reply-to@test.lindsaar.net',
- :date => time)
+ email = BaseMailer.welcome(bcc: 'bcc@test.lindsaar.net',
+ cc: 'cc@test.lindsaar.net',
+ content_type: 'multipart/mixed',
+ charset: 'iso-8559-1',
+ mime_version: '2.0',
+ reply_to: 'reply-to@test.lindsaar.net',
+ date: time)
assert_equal(['bcc@test.lindsaar.net'], email.bcc)
assert_equal(['cc@test.lindsaar.net'], email.cc)
assert_equal('multipart/mixed; charset=iso-8559-1', email.content_type)
@@ -60,7 +60,7 @@ class BaseTest < ActiveSupport::TestCase
end
test "can pass in :body to the mail method hash" do
- email = BaseMailer.welcome(:body => "Hello there")
+ email = BaseMailer.welcome(body: "Hello there")
assert_equal("text/plain", email.mime_type)
assert_equal("Hello there", email.body.encoded)
end
@@ -142,7 +142,7 @@ class BaseTest < ActiveSupport::TestCase
end
test "adds the given :body as part" do
- email = BaseMailer.attachment_with_content(:body => "I'm the eggman")
+ email = BaseMailer.attachment_with_content(body: "I'm the eggman")
assert_equal(2, email.parts.length)
assert_equal("multipart/mixed", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
@@ -165,31 +165,31 @@ class BaseTest < ActiveSupport::TestCase
# Defaults values
test "uses default charset from class" do
- with_default BaseMailer, :charset => "US-ASCII" do
+ with_default BaseMailer, charset: "US-ASCII" do
email = BaseMailer.welcome
assert_equal("US-ASCII", email.charset)
- email = BaseMailer.welcome(:charset => "iso-8559-1")
+ email = BaseMailer.welcome(charset: "iso-8559-1")
assert_equal("iso-8559-1", email.charset)
end
end
test "uses default content type from class" do
- with_default BaseMailer, :content_type => "text/html" do
+ with_default BaseMailer, content_type: "text/html" do
email = BaseMailer.welcome
assert_equal("text/html", email.mime_type)
- email = BaseMailer.welcome(:content_type => "text/plain")
+ email = BaseMailer.welcome(content_type: "text/plain")
assert_equal("text/plain", email.mime_type)
end
end
test "uses default mime version from class" do
- with_default BaseMailer, :mime_version => "2.0" do
+ with_default BaseMailer, mime_version: "2.0" do
email = BaseMailer.welcome
assert_equal("2.0", email.mime_version)
- email = BaseMailer.welcome(:mime_version => "1.0")
+ email = BaseMailer.welcome(mime_version: "1.0")
assert_equal("1.0", email.mime_version)
end
end
@@ -202,17 +202,17 @@ class BaseTest < ActiveSupport::TestCase
end
test "subject gets default from I18n" do
- BaseMailer.default :subject => nil
- email = BaseMailer.welcome(:subject => nil)
+ BaseMailer.default subject: nil
+ email = BaseMailer.welcome(subject: nil)
assert_equal "Welcome", email.subject
- I18n.backend.store_translations('en', :base_mailer => {:welcome => {:subject => "New Subject!"}})
- email = BaseMailer.welcome(:subject => nil)
+ I18n.backend.store_translations('en', base_mailer: {welcome: {subject: "New Subject!"}})
+ email = BaseMailer.welcome(subject: nil)
assert_equal "New Subject!", email.subject
end
test "translations are scoped properly" do
- I18n.backend.store_translations('en', :base_mailer => {:email_with_translations => {:greet_user => "Hello %{name}!"}})
+ I18n.backend.store_translations('en', base_mailer: {email_with_translations: {greet_user: "Hello %{name}!"}})
email = BaseMailer.email_with_translations
assert_equal 'Hello lifo!', email.body.encoded
end
@@ -230,19 +230,19 @@ class BaseTest < ActiveSupport::TestCase
test "implicit multipart with sort order" do
order = ["text/html", "text/plain"]
- with_default BaseMailer, :parts_order => order do
+ with_default BaseMailer, parts_order: order do
email = BaseMailer.implicit_multipart
assert_equal("text/html", email.parts[0].mime_type)
assert_equal("text/plain", email.parts[1].mime_type)
- email = BaseMailer.implicit_multipart(:parts_order => order.reverse)
+ email = BaseMailer.implicit_multipart(parts_order: order.reverse)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("text/html", email.parts[1].mime_type)
end
end
test "implicit multipart with attachments creates nested parts" do
- email = BaseMailer.implicit_multipart(:attachments => true)
+ email = BaseMailer.implicit_multipart(attachments: true)
assert_equal("application/pdf", email.parts[0].mime_type)
assert_equal("multipart/alternative", email.parts[1].mime_type)
assert_equal("text/plain", email.parts[1].parts[0].mime_type)
@@ -253,8 +253,8 @@ class BaseTest < ActiveSupport::TestCase
test "implicit multipart with attachments and sort order" do
order = ["text/html", "text/plain"]
- with_default BaseMailer, :parts_order => order do
- email = BaseMailer.implicit_multipart(:attachments => true)
+ with_default BaseMailer, parts_order: order do
+ email = BaseMailer.implicit_multipart(attachments: true)
assert_equal("application/pdf", email.parts[0].mime_type)
assert_equal("multipart/alternative", email.parts[1].mime_type)
assert_equal("text/plain", email.parts[1].parts[1].mime_type)
@@ -273,7 +273,7 @@ class BaseTest < ActiveSupport::TestCase
end
test "implicit multipart with other locale" do
- swap I18n, :locale => :pl do
+ swap I18n, locale: :pl do
email = BaseMailer.implicit_with_locale
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
@@ -324,19 +324,19 @@ class BaseTest < ActiveSupport::TestCase
test "explicit multipart does not sort order" do
order = ["text/html", "text/plain"]
- with_default BaseMailer, :parts_order => order do
+ with_default BaseMailer, parts_order: order do
email = BaseMailer.explicit_multipart
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("text/html", email.parts[1].mime_type)
- email = BaseMailer.explicit_multipart(:parts_order => order.reverse)
+ email = BaseMailer.explicit_multipart(parts_order: order.reverse)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("text/html", email.parts[1].mime_type)
end
end
test "explicit multipart with attachments creates nested parts" do
- email = BaseMailer.explicit_multipart(:attachments => true)
+ email = BaseMailer.explicit_multipart(attachments: true)
assert_equal("application/pdf", email.parts[0].mime_type)
assert_equal("multipart/alternative", email.parts[1].mime_type)
assert_equal("text/plain", email.parts[1].parts[0].mime_type)
@@ -651,7 +651,7 @@ class BaseTest < ActiveSupport::TestCase
test "default_from can be set" do
class DefaultFromMailer < ActionMailer::Base
- default :to => 'system@test.lindsaar.net'
+ default to: 'system@test.lindsaar.net'
self.default_options = {from: "robert.pankowecki@gmail.com"}
def welcome
diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb
index 7109f23e4c..61a037ea18 100644
--- a/actionmailer/test/delivery_methods_test.rb
+++ b/actionmailer/test/delivery_methods_test.rb
@@ -22,24 +22,24 @@ end
class DefaultsDeliveryMethodsTest < ActiveSupport::TestCase
test "default smtp settings" do
- settings = { :address => "localhost",
- :port => 25,
- :domain => 'localhost.localdomain',
- :user_name => nil,
- :password => nil,
- :authentication => nil,
- :enable_starttls_auto => true }
+ settings = { address: "localhost",
+ port: 25,
+ domain: 'localhost.localdomain',
+ user_name: nil,
+ password: nil,
+ authentication: nil,
+ enable_starttls_auto: true }
assert_equal settings, ActionMailer::Base.smtp_settings
end
test "default file delivery settings" do
- settings = {:location => "#{Dir.tmpdir}/mails"}
+ settings = {location: "#{Dir.tmpdir}/mails"}
assert_equal settings, ActionMailer::Base.file_settings
end
test "default sendmail settings" do
- settings = {:location => '/usr/sbin/sendmail',
- :arguments => '-i -t'}
+ settings = {location: '/usr/sbin/sendmail',
+ arguments: '-i -t'}
assert_equal settings, ActionMailer::Base.sendmail_settings
end
end
@@ -63,8 +63,8 @@ class CustomDeliveryMethodsTest < ActiveSupport::TestCase
end
test "allow to customize custom settings" do
- ActionMailer::Base.custom_settings = { :foo => :bar }
- assert_equal Hash[:foo => :bar], ActionMailer::Base.custom_settings
+ ActionMailer::Base.custom_settings = { foo: :bar }
+ assert_equal Hash[foo: :bar], ActionMailer::Base.custom_settings
end
test "respond to custom settings" do
@@ -82,8 +82,8 @@ end
class MailDeliveryTest < ActiveSupport::TestCase
class DeliveryMailer < ActionMailer::Base
DEFAULT_HEADERS = {
- :to => 'mikel@test.lindsaar.net',
- :from => 'jose@test.plataformatec.com'
+ to: 'mikel@test.lindsaar.net',
+ from: 'jose@test.plataformatec.com'
}
def welcome(hash={})
@@ -110,7 +110,7 @@ class MailDeliveryTest < ActiveSupport::TestCase
test "delivery method can be customized per instance" do
email = DeliveryMailer.welcome.deliver
assert_instance_of Mail::SMTP, email.delivery_method
- email = DeliveryMailer.welcome(:delivery_method => :test).deliver
+ email = DeliveryMailer.welcome(delivery_method: :test).deliver
assert_instance_of Mail::TestMailer, email.delivery_method
end
@@ -125,7 +125,7 @@ class MailDeliveryTest < ActiveSupport::TestCase
test "delivery method options default to class level options" do
default_options = {a: "b"}
ActionMailer::Base.add_delivery_method :optioned, MyOptionedDelivery, default_options
- mail_instance = DeliveryMailer.welcome(:delivery_method => :optioned)
+ mail_instance = DeliveryMailer.welcome(delivery_method: :optioned)
assert_equal default_options, mail_instance.delivery_method.options
end
@@ -133,21 +133,21 @@ class MailDeliveryTest < ActiveSupport::TestCase
default_options = {a: "b"}
ActionMailer::Base.add_delivery_method :optioned, MyOptionedDelivery, default_options
overridden_options = {a: "a"}
- mail_instance = DeliveryMailer.welcome(:delivery_method => :optioned, :delivery_method_options => overridden_options)
+ mail_instance = DeliveryMailer.welcome(delivery_method: :optioned, delivery_method_options: overridden_options)
assert_equal overridden_options, mail_instance.delivery_method.options
end
test "default delivery options can be overridden per mail instance" do
- settings = { :address => "localhost",
- :port => 25,
- :domain => 'localhost.localdomain',
- :user_name => nil,
- :password => nil,
- :authentication => nil,
- :enable_starttls_auto => true }
+ settings = { address: "localhost",
+ port: 25,
+ domain: 'localhost.localdomain',
+ user_name: nil,
+ password: nil,
+ authentication: nil,
+ enable_starttls_auto: true }
assert_equal settings, ActionMailer::Base.smtp_settings
- overridden_options = {user_name: "overridden", :password => "somethingobtuse"}
- mail_instance = DeliveryMailer.welcome(:delivery_method_options => overridden_options)
+ overridden_options = {user_name: "overridden", password: "somethingobtuse"}
+ mail_instance = DeliveryMailer.welcome(delivery_method_options: overridden_options)
delivery_method_instance = mail_instance.delivery_method
assert_equal "overridden", delivery_method_instance.settings[:user_name]
assert_equal "somethingobtuse", delivery_method_instance.settings[:password]
diff --git a/actionmailer/test/i18n_with_controller_test.rb b/actionmailer/test/i18n_with_controller_test.rb
index 68ed86e0d4..a3e93c9c31 100644
--- a/actionmailer/test/i18n_with_controller_test.rb
+++ b/actionmailer/test/i18n_with_controller_test.rb
@@ -9,15 +9,15 @@ class I18nTestMailer < ActionMailer::Base
def mail_with_i18n_subject(recipient)
@recipient = recipient
I18n.locale = :de
- mail(:to => recipient, :subject => "#{I18n.t :email_subject} #{recipient}",
- :from => "system@loudthinking.com", :date => Time.local(2004, 12, 12))
+ mail(to: recipient, subject: "#{I18n.t :email_subject} #{recipient}",
+ from: "system@loudthinking.com", date: Time.local(2004, 12, 12))
end
end
class TestController < ActionController::Base
def send_mail
I18nTestMailer.mail_with_i18n_subject("test@localhost").deliver
- render :text => 'Mail sent'
+ render text: 'Mail sent'
end
end
@@ -32,7 +32,7 @@ class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
end
def setup
- I18n.backend.store_translations('de', :email_subject => '[Signed up] Welcome')
+ I18n.backend.store_translations('de', email_subject: '[Signed up] Welcome')
end
def teardown
diff --git a/actionmailer/test/mail_helper_test.rb b/actionmailer/test/mail_helper_test.rb
index d8a73e6c46..24ccaab8df 100644
--- a/actionmailer/test/mail_helper_test.rb
+++ b/actionmailer/test/mail_helper_test.rb
@@ -10,7 +10,7 @@ class HelperMailer < ActionMailer::Base
"it off!"
mail_with_defaults do |format|
- format.html { render(:inline => "<%= block_format @text %>") }
+ format.html { render(inline: "<%= block_format @text %>") }
end
end
@@ -18,7 +18,7 @@ class HelperMailer < ActionMailer::Base
@text = "But soft! What light through yonder window breaks?"
mail_with_defaults do |format|
- format.html { render(:inline => "<%= format_paragraph @text, 15, 1 %>") }
+ format.html { render(inline: "<%= format_paragraph @text, 15, 1 %>") }
end
end
@@ -26,19 +26,19 @@ class HelperMailer < ActionMailer::Base
@text = "Antidisestablishmentarianism is very long."
mail_with_defaults do |format|
- format.html { render(:inline => "<%= format_paragraph @text, 10, 1 %>") }
+ format.html { render(inline: "<%= format_paragraph @text, 10, 1 %>") }
end
end
def use_mailer
mail_with_defaults do |format|
- format.html { render(:inline => "<%= mailer.message.subject %>") }
+ format.html { render(inline: "<%= mailer.message.subject %>") }
end
end
def use_message
mail_with_defaults do |format|
- format.html { render(:inline => "<%= message.subject %>") }
+ format.html { render(inline: "<%= message.subject %>") }
end
end
@@ -55,15 +55,15 @@ The second
TEXT
mail_with_defaults do |format|
- format.html { render(:inline => "<%= block_format @text %>") }
+ format.html { render(inline: "<%= block_format @text %>") }
end
end
protected
def mail_with_defaults(&block)
- mail(:to => "test@localhost", :from => "tester@example.com",
- :subject => "using helpers", &block)
+ mail(to: "test@localhost", from: "tester@example.com",
+ subject: "using helpers", &block)
end
end
diff --git a/actionmailer/test/mail_layout_test.rb b/actionmailer/test/mail_layout_test.rb
index 71e93c29f1..7f959282cb 100644
--- a/actionmailer/test/mail_layout_test.rb
+++ b/actionmailer/test/mail_layout_test.rb
@@ -1,9 +1,9 @@
require 'abstract_unit'
class AutoLayoutMailer < ActionMailer::Base
- default :to => 'test@localhost',
- :subject => "You have a mail",
- :from => "tester@example.com"
+ default to: 'test@localhost',
+ subject: "You have a mail",
+ from: "tester@example.com"
def hello
mail()
@@ -11,16 +11,16 @@ class AutoLayoutMailer < ActionMailer::Base
def spam
@world = "Earth"
- mail(:body => render(:inline => "Hello, <%= @world %>", :layout => 'spam'))
+ mail(body: render(inline: "Hello, <%= @world %>", layout: 'spam'))
end
def nolayout
@world = "Earth"
- mail(:body => render(:inline => "Hello, <%= @world %>", :layout => false))
+ mail(body: render(inline: "Hello, <%= @world %>", layout: false))
end
def multipart(type = nil)
- mail(:content_type => type) do |format|
+ mail(content_type: type) do |format|
format.text { render }
format.html { render }
end
@@ -28,11 +28,11 @@ class AutoLayoutMailer < ActionMailer::Base
end
class ExplicitLayoutMailer < ActionMailer::Base
- layout 'spam', :except => [:logout]
+ layout 'spam', except: [:logout]
- default :to => 'test@localhost',
- :subject => "You have a mail",
- :from => "tester@example.com"
+ default to: 'test@localhost',
+ subject: "You have a mail",
+ from: "tester@example.com"
def signup
mail()
diff --git a/actionmailer/test/mailers/base_mailer.rb b/actionmailer/test/mailers/base_mailer.rb
index e55d72fdb4..f25d9b9aff 100644
--- a/actionmailer/test/mailers/base_mailer.rb
+++ b/actionmailer/test/mailers/base_mailer.rb
@@ -1,13 +1,13 @@
class BaseMailer < ActionMailer::Base
self.mailer_name = "base_mailer"
- default :to => 'system@test.lindsaar.net',
- :from => 'jose@test.plataformatec.com',
- :reply_to => 'mikel@test.lindsaar.net'
+ default to: 'system@test.lindsaar.net',
+ from: 'jose@test.plataformatec.com',
+ reply_to: 'mikel@test.lindsaar.net'
def welcome(hash = {})
headers['X-SPAM'] = "Not SPAM"
- mail({:subject => "The first email on new API!"}.merge!(hash))
+ mail({subject: "The first email on new API!"}.merge!(hash))
end
def welcome_with_headers(hash = {})
@@ -16,7 +16,7 @@ class BaseMailer < ActionMailer::Base
end
def welcome_from_another_path(path)
- mail(:template_name => "welcome", :template_path => path)
+ mail(template_name: "welcome", template_path: path)
end
def html_only(hash = {})
@@ -38,15 +38,15 @@ class BaseMailer < ActionMailer::Base
end
def attachment_with_hash
- attachments['invoice.jpg'] = { :data => "\312\213\254\232)b",
- :mime_type => "image/x-jpg",
- :transfer_encoding => "base64" }
+ attachments['invoice.jpg'] = { data: "\312\213\254\232)b",
+ mime_type: "image/x-jpg",
+ transfer_encoding: "base64" }
mail
end
def attachment_with_hash_default_encoding
- attachments['invoice.jpg'] = { :data => "\312\213\254\232)b",
- :mime_type => "image/x-jpg" }
+ attachments['invoice.jpg'] = { data: "\312\213\254\232)b",
+ mime_type: "image/x-jpg" }
mail
end
@@ -62,8 +62,8 @@ class BaseMailer < ActionMailer::Base
def explicit_multipart(hash = {})
attachments['invoice.pdf'] = 'This is test File content' if hash.delete(:attachments)
mail(hash) do |format|
- format.text { render :text => "TEXT Explicit Multipart" }
- format.html { render :text => "HTML Explicit Multipart" }
+ format.text { render text: "TEXT Explicit Multipart" }
+ format.html { render text: "HTML Explicit Multipart" }
end
end
@@ -76,13 +76,13 @@ class BaseMailer < ActionMailer::Base
def explicit_multipart_with_any(hash = {})
mail(hash) do |format|
- format.any(:text, :html){ render :text => "Format with any!" }
+ format.any(:text, :html){ render text: "Format with any!" }
end
end
def explicit_multipart_with_options(include_html = false)
mail do |format|
- format.text(:content_transfer_encoding => "base64"){ render "welcome" }
+ format.text(content_transfer_encoding: "base64"){ render "welcome" }
format.html{ render "welcome" } if include_html
end
end
@@ -95,24 +95,24 @@ class BaseMailer < ActionMailer::Base
end
def implicit_different_template(template_name='')
- mail(:template_name => template_name)
+ mail(template_name: template_name)
end
def explicit_different_template(template_name='')
mail do |format|
- format.text { render :template => "#{mailer_name}/#{template_name}" }
- format.html { render :template => "#{mailer_name}/#{template_name}" }
+ format.text { render template: "#{mailer_name}/#{template_name}" }
+ format.html { render template: "#{mailer_name}/#{template_name}" }
end
end
def different_layout(layout_name='')
mail do |format|
- format.text { render :layout => layout_name }
- format.html { render :layout => layout_name }
+ format.text { render layout: layout_name }
+ format.html { render layout: layout_name }
end
end
def email_with_translations
- mail :body => render("email_with_translations", :formats => [:html])
+ mail body: render("email_with_translations", formats: [:html])
end
end
diff --git a/actionmailer/test/mailers/proc_mailer.rb b/actionmailer/test/mailers/proc_mailer.rb
index 43916e1421..733633b575 100644
--- a/actionmailer/test/mailers/proc_mailer.rb
+++ b/actionmailer/test/mailers/proc_mailer.rb
@@ -1,7 +1,7 @@
class ProcMailer < ActionMailer::Base
- default :to => 'system@test.lindsaar.net',
+ default to: 'system@test.lindsaar.net',
'X-Proc-Method' => Proc.new { Time.now.to_i.to_s },
- :subject => Proc.new { give_a_greeting }
+ subject: Proc.new { give_a_greeting }
def welcome
mail
diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb
index dd62164176..7c7f0b6fdc 100644
--- a/actionmailer/test/test_helper_test.rb
+++ b/actionmailer/test/test_helper_test.rb
@@ -3,9 +3,9 @@ require 'abstract_unit'
class TestHelperMailer < ActionMailer::Base
def test
@world = "Earth"
- mail :body => render(:inline => "Hello, <%= @world %>"),
- :to => "test@example.com",
- :from => "tester@example.com"
+ mail body: render(inline: "Hello, <%= @world %>"),
+ to: "test@example.com",
+ from: "tester@example.com"
end
end
diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/url_test.rb
index 2ea1723434..dcd80f46b5 100644
--- a/actionmailer/test/url_test.rb
+++ b/actionmailer/test/url_test.rb
@@ -19,9 +19,9 @@ class UrlTestMailer < ActionMailer::Base
def signed_up_with_url(recipient)
@recipient = recipient
- @welcome_url = url_for :host => "example.com", :controller => "welcome", :action => "greeting"
- mail(:to => recipient, :subject => "[Signed up] Welcome #{recipient}",
- :from => "system@loudthinking.com", :date => Time.local(2004, 12, 12))
+ @welcome_url = url_for host: "example.com", controller: "welcome", action: "greeting"
+ mail(to: recipient, subject: "[Signed up] Welcome #{recipient}",
+ from: "system@loudthinking.com", date: Time.local(2004, 12, 12))
end
end
@@ -58,7 +58,7 @@ class ActionMailerUrlTest < ActionMailer::TestCase
AppRoutes.draw do
get ':controller(/:action(/:id))'
- get '/welcome' => "foo#bar", :as => "welcome"
+ get '/welcome' => "foo#bar", as: "welcome"
end
expected = new_mail
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 490eb17df3..48ba1518e0 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,86 @@
## Rails 4.0.0 (unreleased) ##
+* `date_select` helper accepts `with_css_classes: true` to add css classes similar with type
+ of generated select tags.
+
+ *Pavel Nikitin*
+
+* Only non-js/css under app/assets path will be included in default config.assets.precompile.
+
+ *Josh Peek*
+
+* Remove support for the RAILS_ASSET_ID environment configuration
+ (no longer needed now that we have the asset pipeline).
+
+ *Josh Peek*
+
+* Remove old asset_path configuration (no longer needed now that we have the asset pipeline).
+
+ *Josh Peek*
+
+* `assert_template` can be used to assert on the same template with different locals
+ Fix #3675
+
+ *Yves Senn*
+
+* Remove old asset tag concatenation (no longer needed now that we have the asset pipeline).
+
+ *Josh Peek*
+
+* Accept :remote as symbolic option for `link_to` helper. *Riley Lynch*
+
+* Warn when the `:locals` option is passed to `assert_template` outside of a view test case
+ Fix #3415
+
+ *Yves Senn*
+
+* The `Rack::Cache` middleware is now disabled by default. To enable it,
+ set `config.action_dispatch.rack_cache = true` and add `gem rack-cache` to your Gemfile.
+
+ *Guillermo Iguaran*
+
+* `ActionController::Base.page_cache_extension` option is deprecated
+ in favour of `ActionController::Base.default_static_extension`.
+
+ *Francesco Rodriguez*
+
+* Action and Page caching has been extracted from Action Dispatch
+ as `actionpack-action_caching` and `actionpack-page_caching` gems.
+ Please read the `README.md` file on both gems for the usage.
+
+ *Francesco Rodriguez*
+
+* Failsafe exception returns text/plain. *Steve Klabnik*
+
+* Remove `rack-cache` dependency from Action Pack and declare it on Gemfile
+
+ *Guillermo Iguaran*
+
+* Rename internal variables on ActionController::TemplateAssertions to prevent
+ naming collisions. @partials, @templates and @layouts are now prefixed with an underscore.
+ Fix #7459
+
+ *Yves Senn*
+
+* `resource` and `resources` don't modify the passed options hash
+ Fix #7777
+
+ *Yves Senn*
+
+* Precompiled assets include aliases from foo.js to foo/index.js and vice versa.
+
+ # Precompiles phone-<digest>.css and aliases phone/index.css to phone.css.
+ config.assets.precompile = [ 'phone.css' ]
+
+ # Precompiles phone/index-<digest>.css and aliases phone.css to phone/index.css.
+ config.assets.precompile = [ 'phone/index.css' ]
+
+ # Both of these work with either precompile thanks to their aliases.
+ <%= stylesheet_link_tag 'phone', media: 'all' %>
+ <%= stylesheet_link_tag 'phone/index', media: 'all' %>
+
+ *Jeremy Kemper*
+
* `assert_template` is no more passing with what ever string that matches
with the template name.
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index fd09d3b55b..7d292ac17c 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -18,7 +18,6 @@ Gem::Specification.new do |s|
s.requirements << 'none'
s.add_dependency('activesupport', version)
- s.add_dependency('rack-cache', '~> 1.2')
s.add_dependency('builder', '~> 3.1.0')
s.add_dependency('rack', '~> 1.4.1')
s.add_dependency('rack-test', '~> 0.6.1')
diff --git a/actionpack/lib/abstract_controller/asset_paths.rb b/actionpack/lib/abstract_controller/asset_paths.rb
index 822254b1a4..e6170228d9 100644
--- a/actionpack/lib/abstract_controller/asset_paths.rb
+++ b/actionpack/lib/abstract_controller/asset_paths.rb
@@ -3,7 +3,7 @@ module AbstractController
extend ActiveSupport::Concern
included do
- config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir,
+ config_accessor :asset_host, :assets_dir, :javascripts_dir,
:stylesheets_dir, :default_asset_host_protocol, :relative_url_root
end
end
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index 9c3960961b..388e043f0b 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -217,8 +217,10 @@ module AbstractController
# * <tt>string</tt> - The name of the method that handles the action
# * <tt>nil</tt> - No method name could be found. Raise ActionNotFound.
def method_for_action(action_name)
- if action_method?(action_name) then action_name
- elsif respond_to?(:action_missing, true) then "_handle_action_missing"
+ if action_method?(action_name)
+ action_name
+ elsif respond_to?(:action_missing, true)
+ "_handle_action_missing"
end
end
end
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 6b8d9384d4..9b3bf99fc3 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -43,7 +43,7 @@ module ActionController
#
# def server_ip
# location = request.env["SERVER_ADDR"]
- # render :text => "This server hosted at #{location}"
+ # render text: "This server hosted at #{location}"
# end
#
# == Parameters
@@ -113,9 +113,9 @@ module ActionController
# def search
# @results = Search.find(params[:query])
# case @results.count
- # when 0 then render :action => "no_results"
- # when 1 then render :action => "show"
- # when 2..10 then render :action => "show_many"
+ # when 0 then render action: "no_results"
+ # when 1 then render action: "show"
+ # when 2..10 then render action: "show_many"
# end
# end
#
@@ -131,7 +131,7 @@ module ActionController
# @entry = Entry.new(params[:entry])
# if @entry.save
# # The entry was saved correctly, redirect to show
- # redirect_to :action => 'show', :id => @entry.id
+ # redirect_to action: 'show', id: @entry.id
# else
# # things didn't go so well, do something else
# end
@@ -148,15 +148,15 @@ module ActionController
# An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
#
# def do_something
- # redirect_to :action => "elsewhere"
- # render :action => "overthere" # raises DoubleRenderError
+ # redirect_to action: "elsewhere"
+ # render action: "overthere" # raises DoubleRenderError
# end
#
# If you need to redirect on the condition of something, then be sure to add "and return" to halt execution.
#
# def do_something
- # redirect_to(:action => "elsewhere") and return if monkeys.nil?
- # render :action => "overthere" # won't be called if monkeys is nil
+ # redirect_to(action: "elsewhere") and return if monkeys.nil?
+ # render action: "overthere" # won't be called if monkeys is nil
# end
#
class Base < Metal
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index fc27a0774b..462f147371 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -2,11 +2,9 @@ require 'fileutils'
require 'uri'
require 'set'
-module ActionController #:nodoc:
+module ActionController
# \Caching is a cheap way of speeding up slow applications by keeping the result of
# calculations, renderings, and database calls around for subsequent requests.
- # Action Controller affords you three approaches in varying levels of granularity:
- # Page, Action, Fragment.
#
# You can read more about each approach and the sweeping assistance by clicking the
# modules below.
@@ -17,8 +15,7 @@ module ActionController #:nodoc:
# == \Caching stores
#
# All the caching stores from ActiveSupport::Cache are available to be used as backends
- # for Action Controller caching. This setting only affects action and fragment caching
- # as page caching is always written to disk.
+ # for Action Controller caching.
#
# Configuration examples (MemoryStore is the default):
#
@@ -32,9 +29,7 @@ module ActionController #:nodoc:
extend ActiveSupport::Autoload
eager_autoload do
- autoload :Actions
autoload :Fragments
- autoload :Pages
autoload :Sweeper, 'action_controller/caching/sweeping'
autoload :Sweeping, 'action_controller/caching/sweeping'
end
@@ -58,12 +53,25 @@ module ActionController #:nodoc:
include AbstractController::Callbacks
include ConfigMethods
- include Pages, Actions, Fragments
+ include Fragments
include Sweeping if defined?(ActiveRecord)
included do
extend ConfigMethods
+ config_accessor :default_static_extension
+ self.default_static_extension ||= '.html'
+
+ def self.page_cache_extension=(extension)
+ ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
+ self.default_static_extension = extension
+ end
+
+ def self.page_cache_extension
+ ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
+ default_static_extension
+ end
+
config_accessor :perform_caching
self.perform_caching = true if perform_caching.nil?
end
diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb
deleted file mode 100644
index bf16fe267c..0000000000
--- a/actionpack/lib/action_controller/caching/actions.rb
+++ /dev/null
@@ -1,189 +0,0 @@
-require 'set'
-
-module ActionController
- module Caching
- # Action caching is similar to page caching by the fact that the entire
- # output of the response is cached, but unlike page caching, every
- # request still goes through Action Pack. The key benefit of this is
- # that filters run before the cache is served, which allows for
- # authentication and other restrictions on whether someone is allowed
- # to execute such action.
- #
- # class ListsController < ApplicationController
- # before_filter :authenticate, except: :public
- #
- # caches_page :public
- # caches_action :index, :show
- # end
- #
- # In this example, the +public+ action doesn't require authentication
- # so it's possible to use the faster page caching. On the other hand
- # +index+ and +show+ require authentication. They can still be cached,
- # but we need action caching for them.
- #
- # Action caching uses fragment caching internally and an around
- # filter to do the job. The fragment cache is named according to
- # the host and path of the request. A page that is accessed at
- # <tt>http://david.example.com/lists/show/1</tt> will result in a fragment named
- # <tt>david.example.com/lists/show/1</tt>. This allows the cacher to
- # differentiate between <tt>david.example.com/lists/</tt> and
- # <tt>jamis.example.com/lists/</tt> -- which is a helpful way of assisting
- # the subdomain-as-account-key pattern.
- #
- # Different representations of the same resource, e.g.
- # <tt>http://david.example.com/lists</tt> and
- # <tt>http://david.example.com/lists.xml</tt>
- # are treated like separate requests and so are cached separately.
- # Keep in mind when expiring an action cache that
- # <tt>action: 'lists'</tt> is not the same as
- # <tt>action: 'list', format: :xml</tt>.
- #
- # You can modify the default action cache path by passing a
- # <tt>:cache_path</tt> option. This will be passed directly to
- # <tt>ActionCachePath.new</tt>. This is handy for actions with
- # multiple possible routes that should be cached differently. If a
- # block is given, it is called with the current controller instance.
- #
- # And you can also use <tt>:if</tt> (or <tt>:unless</tt>) to pass a
- # proc that specifies when the action should be cached.
- #
- # As of Rails 3.0, you can also pass <tt>:expires_in</tt> with a time
- # interval (in seconds) to schedule expiration of the cached item.
- #
- # The following example depicts some of the points made above:
- #
- # class ListsController < ApplicationController
- # before_filter :authenticate, except: :public
- #
- # caches_page :public
- #
- # caches_action :index, if: Proc.new do
- # !request.format.json? # cache if is not a JSON request
- # end
- #
- # caches_action :show, cache_path: { project: 1 },
- # expires_in: 1.hour
- #
- # caches_action :feed, cache_path: Proc.new do
- # if params[:user_id]
- # user_list_url(params[:user_id, params[:id])
- # else
- # list_url(params[:id])
- # end
- # end
- # end
- #
- # If you pass <tt>layout: false</tt>, it will only cache your action
- # content. That's useful when your layout has dynamic information.
- #
- # Warning: If the format of the request is determined by the Accept HTTP
- # header the Content-Type of the cached response could be wrong because
- # no information about the MIME type is stored in the cache key. So, if
- # you first ask for MIME type M in the Accept header, a cache entry is
- # created, and then perform a second request to the same resource asking
- # for a different MIME type, you'd get the content cached for M.
- #
- # The <tt>:format</tt> parameter is taken into account though. The safest
- # way to cache by MIME type is to pass the format in the route.
- module Actions
- extend ActiveSupport::Concern
-
- module ClassMethods
- # Declares that +actions+ should be cached.
- # See ActionController::Caching::Actions for details.
- def caches_action(*actions)
- return unless cache_configured?
- options = actions.extract_options!
- options[:layout] = true unless options.key?(:layout)
- filter_options = options.extract!(:if, :unless).merge(:only => actions)
- cache_options = options.extract!(:layout, :cache_path).merge(:store_options => options)
-
- around_filter ActionCacheFilter.new(cache_options), filter_options
- end
- end
-
- def _save_fragment(name, options)
- content = ""
- response_body.each do |parts|
- content << parts
- end
-
- if caching_allowed?
- write_fragment(name, content, options)
- else
- content
- end
- end
-
- protected
- def expire_action(options = {})
- return unless cache_configured?
-
- if options.is_a?(Hash) && options[:action].is_a?(Array)
- options[:action].each {|action| expire_action(options.merge(:action => action)) }
- else
- expire_fragment(ActionCachePath.new(self, options, false).path)
- end
- end
-
- class ActionCacheFilter #:nodoc:
- def initialize(options, &block)
- @cache_path, @store_options, @cache_layout =
- options.values_at(:cache_path, :store_options, :layout)
- end
-
- def around(controller)
- cache_layout = @cache_layout.respond_to?(:call) ? @cache_layout.call(controller) : @cache_layout
-
- path_options = if @cache_path.respond_to?(:call)
- controller.instance_exec(controller, &@cache_path)
- else
- @cache_path
- end
-
- cache_path = ActionCachePath.new(controller, path_options || {})
-
- body = controller.read_fragment(cache_path.path, @store_options)
-
- unless body
- controller.action_has_layout = false unless cache_layout
- yield
- controller.action_has_layout = true
- body = controller._save_fragment(cache_path.path, @store_options)
- end
-
- body = controller.render_to_string(:text => body, :layout => true) unless cache_layout
-
- controller.response_body = body
- controller.content_type = Mime[cache_path.extension || :html]
- end
- end
-
- class ActionCachePath
- attr_reader :path, :extension
-
- # If +infer_extension+ is +true+, the cache path extension is looked up from the request's
- # path and format. This is desirable when reading and writing the cache, but not when
- # expiring the cache - +expire_action+ should expire the same files regardless of the
- # request format.
- def initialize(controller, options = {}, infer_extension = true)
- if infer_extension
- @extension = controller.params[:format]
- options.reverse_merge!(:format => @extension) if options.is_a?(Hash)
- end
-
- path = controller.url_for(options).split('://', 2).last
- @path = normalize!(path)
- end
-
- private
- def normalize!(path)
- ext = URI.parser.escape(extension) if extension
- path << 'index' if path[-1] == ?/
- path << ".#{ext}" if extension and !path.split('?', 2).first.ends_with?(".#{ext}")
- URI.parser.unescape(path)
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
deleted file mode 100644
index 3cf8d965ff..0000000000
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ /dev/null
@@ -1,202 +0,0 @@
-require 'fileutils'
-require 'active_support/core_ext/class/attribute_accessors'
-
-module ActionController
- module Caching
- # Page caching is an approach to caching where the entire action output of is
- # stored as a HTML file that the web server can serve without going through
- # Action Pack. This is the fastest way to cache your content as opposed to going
- # dynamically through the process of generating the content. Unfortunately, this
- # incredible speed-up is only available to stateless pages where all visitors are
- # treated the same. Content management systems -- including weblogs and wikis --
- # have many pages that are a great fit for this approach, but account-based systems
- # where people log in and manipulate their own data are often less likely candidates.
- #
- # Specifying which actions to cache is done through the +caches_page+ class method:
- #
- # class WeblogController < ActionController::Base
- # caches_page :show, :new
- # end
- #
- # This will generate cache files such as <tt>weblog/show/5.html</tt> and
- # <tt>weblog/new.html</tt>, which match the URLs used that would normally trigger
- # dynamic page generation. Page caching works by configuring a web server to first
- # check for the existence of files on disk, and to serve them directly when found,
- # without passing the request through to Action Pack. This is much faster than
- # handling the full dynamic request in the usual way.
- #
- # Expiration of the cache is handled by deleting the cached file, which results
- # in a lazy regeneration approach where the cache is not restored before another
- # hit is made against it. The API for doing so mimics the options from +url_for+ and friends:
- #
- # class WeblogController < ActionController::Base
- # def update
- # List.update(params[:list][:id], params[:list])
- # expire_page action: 'show', id: params[:list][:id]
- # redirect_to action: 'show', id: params[:list][:id]
- # end
- # end
- #
- # Additionally, you can expire caches using Sweepers that act on changes in
- # the model to determine when a cache is supposed to be expired.
- module Pages
- extend ActiveSupport::Concern
-
- included do
- # The cache directory should be the document root for the web server and is
- # set using <tt>Base.page_cache_directory = "/document/root"</tt>. For Rails,
- # this directory has already been set to Rails.public_path (which is usually
- # set to <tt>Rails.root + "/public"</tt>). Changing this setting can be useful
- # to avoid naming conflicts with files in <tt>public/</tt>, but doing so will
- # likely require configuring your web server to look in the new location for
- # cached files.
- class_attribute :page_cache_directory
- self.page_cache_directory ||= ''
-
- # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>.
- # In these cases, the page caching mechanism will add one in order to make it
- # easy for the cached files to be picked up properly by the web server. By
- # default, this cache extension is <tt>.html</tt>. If you want something else,
- # like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension.
- # In cases where a request already has an extension, such as <tt>.xml</tt>
- # or <tt>.rss</tt>, page caching will not add an extension. This allows it
- # to work well with RESTful apps.
- class_attribute :page_cache_extension
- self.page_cache_extension ||= '.html'
-
- # The compression used for gzip. If +false+ (default), the page is not compressed.
- # If can be a symbol showing the ZLib compression method, for example, <tt>:best_compression</tt>
- # or <tt>:best_speed</tt> or an integer configuring the compression level.
- class_attribute :page_cache_compression
- self.page_cache_compression ||= false
- end
-
- module ClassMethods
- # Expires the page that was cached with the +path+ as a key.
- #
- # expire_page '/lists/show'
- def expire_page(path)
- return unless perform_caching
- path = page_cache_path(path)
-
- instrument_page_cache :expire_page, path do
- File.delete(path) if File.exist?(path)
- File.delete(path + '.gz') if File.exist?(path + '.gz')
- end
- end
-
- # Manually cache the +content+ in the key determined by +path+.
- #
- # cache_page "I'm the cached content", '/lists/show'
- def cache_page(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION)
- return unless perform_caching
- path = page_cache_path(path, extension)
-
- instrument_page_cache :write_page, path do
- FileUtils.makedirs(File.dirname(path))
- File.open(path, "wb+") { |f| f.write(content) }
- if gzip
- Zlib::GzipWriter.open(path + '.gz', gzip) { |f| f.write(content) }
- end
- end
- end
-
- # Caches the +actions+ using the page-caching approach that'll store
- # the cache in a path within the +page_cache_directory+ that
- # matches the triggering url.
- #
- # You can also pass a <tt>:gzip</tt> option to override the class configuration one.
- #
- # # cache the index action
- # caches_page :index
- #
- # # cache the index action except for JSON requests
- # caches_page :index, if: Proc.new { !request.format.json? }
- #
- # # don't gzip images
- # caches_page :image, gzip: false
- def caches_page(*actions)
- return unless perform_caching
- options = actions.extract_options!
-
- gzip_level = options.fetch(:gzip, page_cache_compression)
- gzip_level = case gzip_level
- when Symbol
- Zlib.const_get(gzip_level.upcase)
- when Fixnum
- gzip_level
- when false
- nil
- else
- Zlib::BEST_COMPRESSION
- end
-
- after_filter({:only => actions}.merge(options)) do |c|
- c.cache_page(nil, nil, gzip_level)
- end
- end
-
- private
- def page_cache_file(path, extension)
- name = (path.empty? || path == "/") ? "/index" : URI.parser.unescape(path.chomp('/'))
- unless (name.split('/').last || name).include? '.'
- name << (extension || self.page_cache_extension)
- end
- return name
- end
-
- def page_cache_path(path, extension = nil)
- page_cache_directory.to_s + page_cache_file(path, extension)
- end
-
- def instrument_page_cache(name, path)
- ActiveSupport::Notifications.instrument("#{name}.action_controller", :path => path){ yield }
- end
- end
-
- # Expires the page that was cached with the +options+ as a key.
- #
- # expire_page controller: 'lists', action: 'show'
- def expire_page(options = {})
- return unless self.class.perform_caching
-
- if options.is_a?(Hash)
- if options[:action].is_a?(Array)
- options[:action].each do |action|
- self.class.expire_page(url_for(options.merge(:only_path => true, :action => action)))
- end
- else
- self.class.expire_page(url_for(options.merge(:only_path => true)))
- end
- else
- self.class.expire_page(options)
- end
- end
-
- # Manually cache the +content+ in the key determined by +options+. If no content is provided,
- # the contents of response.body is used. If no options are provided, the url of the current
- # request being handled is used.
- #
- # cache_page "I'm the cached content", controller: 'lists', action: 'show'
- def cache_page(content = nil, options = nil, gzip = Zlib::BEST_COMPRESSION)
- return unless self.class.perform_caching && caching_allowed?
-
- path = case options
- when Hash
- url_for(options.merge(:only_path => true, :format => params[:format]))
- when String
- options
- else
- request.path
- end
-
- if (type = Mime::LOOKUP[self.content_type]) && (type_symbol = type.symbol).present?
- extension = ".#{type_symbol}"
- end
-
- self.class.cache_page(content || response.body, path, extension, gzip)
- end
-
- end
- end
-end
diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb
index f41d1bb4b9..3d274e7dd7 100644
--- a/actionpack/lib/action_controller/log_subscriber.rb
+++ b/actionpack/lib/action_controller/log_subscriber.rb
@@ -4,6 +4,8 @@ module ActionController
INTERNAL_PARAMS = %w(controller action format _method only_path)
def start_processing(event)
+ return unless logger.info?
+
payload = event.payload
params = payload[:params].except(*INTERNAL_PARAMS)
format = payload[:format]
@@ -14,6 +16,8 @@ module ActionController
end
def process_action(event)
+ return unless logger.info?
+
payload = event.payload
additions = ActionController::Base.log_process_action(payload)
@@ -22,35 +26,36 @@ module ActionController
exception_class_name = payload[:exception].first
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
end
- message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration
+ message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
message << " (#{additions.join(" | ")})" unless additions.blank?
info(message)
end
def halted_callback(event)
- info "Filter chain halted as #{event.payload[:filter]} rendered or redirected"
+ info("Filter chain halted as #{event.payload[:filter]} rendered or redirected")
end
def send_file(event)
- info("Sent file %s (%.1fms)" % [event.payload[:path], event.duration])
+ info("Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)")
end
def redirect_to(event)
- info "Redirected to #{event.payload[:location]}"
+ info("Redirected to #{event.payload[:location]}")
end
def send_data(event)
- info("Sent data %s (%.1fms)" % [event.payload[:filename], event.duration])
+ info("Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)")
end
%w(write_fragment read_fragment exist_fragment?
expire_fragment expire_page write_page).each do |method|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{method}(event)
+ return unless logger.info?
key_or_path = event.payload[:key] || event.payload[:path]
human_name = #{method.to_s.humanize.inspect}
- info("\#{human_name} \#{key_or_path} \#{"(%.1fms)" % event.duration}")
+ info("\#{human_name} \#{key_or_path} (\#{event.duration.round(1)}ms)")
end
METHOD
end
diff --git a/actionpack/lib/action_controller/metal/hide_actions.rb b/actionpack/lib/action_controller/metal/hide_actions.rb
index 420b22cf56..2aa6b7adaf 100644
--- a/actionpack/lib/action_controller/metal/hide_actions.rb
+++ b/actionpack/lib/action_controller/metal/hide_actions.rb
@@ -26,20 +26,14 @@ module ActionController
self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s)).freeze
end
- def inherited(klass)
- klass.class_eval { @visible_actions = {} }
- super
- end
-
def visible_action?(action_name)
- return @visible_actions[action_name] if @visible_actions.key?(action_name)
- @visible_actions[action_name] = !hidden_actions.include?(action_name)
+ action_methods.include?(action_name)
end
# Overrides AbstractController::Base#action_methods to remove any methods
# that are listed as hidden methods.
def action_methods
- @action_methods ||= Set.new(super.reject { |name| hidden_actions.include?(name) })
+ @action_methods ||= Set.new(super.reject { |name| hidden_actions.include?(name) }).freeze
end
end
end
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index c9a81e4866..e33201b273 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -112,6 +112,11 @@ module ActionController
# params.permitted? # => true
# Person.new(params) # => #<Person id: nil, name: "Francesco">
def permit!
+ each_pair do |key, value|
+ convert_hashes_to_parameters(key, value)
+ self[key].permit! if self[key].respond_to? :permit!
+ end
+
@permitted = true
self
end
@@ -166,13 +171,39 @@ module ActionController
# permitted[:person][:age] # => nil
# permitted[:person][:pets][0][:name] # => "Purplish"
# permitted[:person][:pets][0][:category] # => nil
+ #
+ # Note that if you use +permit+ in a key that points to a hash,
+ # it won't allow all the hash. You also need to specify which
+ # attributes inside the hash should be whitelisted.
+ #
+ # params = ActionController::Parameters.new({
+ # person: {
+ # contact: {
+ # email: 'none@test.com'
+ # phone: '555-1234'
+ # }
+ # }
+ # })
+ #
+ # params.require(:person).permit(:contact)
+ # # => {}
+ #
+ # params.require(:person).permit(contact: :phone)
+ # # => {"contact"=>{"phone"=>"555-1234"}}
+ #
+ # params.require(:person).permit(contact: [ :email, :phone ])
+ # # => {"contact"=>{"email"=>"none@test.com", "phone"=>"555-1234"}}
def permit(*filters)
params = self.class.new
filters.each do |filter|
case filter
when Symbol, String then
- params[filter] = self[filter] if has_key?(filter)
+ if has_key?(filter)
+ _value = self[filter]
+ params[filter] = _value unless Hash === _value
+ end
+ keys.grep(/\A#{Regexp.escape(filter)}\(\d+[if]?\)\z/) { |key| params[key] = self[key] }
when Hash then
self.slice(*filter.keys).each do |key, values|
return unless values
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index f2c68432c1..3e44155f73 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -32,10 +32,8 @@ module ActionController
options.javascripts_dir ||= paths["public/javascripts"].first
options.stylesheets_dir ||= paths["public/stylesheets"].first
- options.page_cache_directory ||= paths["public"].first
# Ensure readers methods get compiled
- options.asset_path ||= app.config.asset_path
options.asset_host ||= app.config.asset_host
options.relative_url_root ||= app.config.relative_url_root
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 20c6e2bf53..d911d47a1d 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -12,16 +12,16 @@ module ActionController
end
def setup_subscriptions
- @partials = Hash.new(0)
- @templates = Hash.new(0)
- @layouts = Hash.new(0)
+ @_partials = Hash.new(0)
+ @_templates = Hash.new(0)
+ @_layouts = Hash.new(0)
ActiveSupport::Notifications.subscribe("render_template.action_view") do |name, start, finish, id, payload|
path = payload[:layout]
if path
- @layouts[path] += 1
+ @_layouts[path] += 1
if path =~ /^layouts\/(.*)/
- @layouts[$1] += 1
+ @_layouts[$1] += 1
end
end
end
@@ -32,11 +32,11 @@ module ActionController
partial = path =~ /^.*\/_[^\/]*$/
if partial
- @partials[path] += 1
- @partials[path.split("/").last] += 1
+ @_partials[path] += 1
+ @_partials[path.split("/").last] += 1
end
- @templates[path] += 1
+ @_templates[path] += 1
end
end
@@ -46,9 +46,9 @@ module ActionController
end
def process(*args)
- @partials = Hash.new(0)
- @templates = Hash.new(0)
- @layouts = Hash.new(0)
+ @_partials = Hash.new(0)
+ @_templates = Hash.new(0)
+ @_layouts = Hash.new(0)
super
end
@@ -88,7 +88,7 @@ module ActionController
case options
when NilClass, Regexp, String, Symbol
options = options.to_s if Symbol === options
- rendered = @templates
+ rendered = @_templates
msg = message || sprintf("expecting <%s> but rendering with <%s>",
options.inspect, rendered.keys)
matches_template =
@@ -109,36 +109,41 @@ module ActionController
if options.key?(:layout)
expected_layout = options[:layout]
msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
- expected_layout, @layouts.keys)
+ expected_layout, @_layouts.keys)
case expected_layout
when String, Symbol
- assert_includes @layouts.keys, expected_layout.to_s, msg
+ assert_includes @_layouts.keys, expected_layout.to_s, msg
when Regexp
- assert(@layouts.keys.any? {|l| l =~ expected_layout }, msg)
+ assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg)
when nil, false
- assert(@layouts.empty?, msg)
+ assert(@_layouts.empty?, msg)
end
end
if expected_partial = options[:partial]
if expected_locals = options[:locals]
- actual_locals = @locals[expected_partial.to_s.sub(/^_/,'')]
- expected_locals.each_pair do |k,v|
- assert_equal(v, actual_locals[k])
+ if defined?(@_rendered_views)
+ view = expected_partial.to_s.sub(/^_/,'')
+ 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]
+ 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
+ options[:partial], @_partials.keys)
+ assert_includes @_partials, expected_partial, msg
end
elsif options.key?(:partial)
- assert @partials.empty?,
+ assert @_partials.empty?,
"Expected no partials to be rendered"
end
else
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index 47cf41cfa3..4a7df6b657 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -1,3 +1,4 @@
+require 'mutex_m'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/object/duplicable'
@@ -20,9 +21,18 @@ module ActionDispatch
# end
# => reverses the value to all keys matching /secret/i
module FilterParameters
- extend ActiveSupport::Concern
+ @@parameter_filter_for = {}.extend(Mutex_m)
- @@parameter_filter_for = {}
+ ENV_MATCH = [/RAW_POST_DATA/, "rack.request.form_vars"] # :nodoc:
+ NULL_PARAM_FILTER = ParameterFilter.new # :nodoc:
+ NULL_ENV_FILTER = ParameterFilter.new ENV_MATCH # :nodoc:
+
+ def initialize(env)
+ super
+ @filtered_parameters = nil
+ @filtered_env = nil
+ @filtered_path = nil
+ end
# Return a hash of parameters with all sensitive data replaced.
def filtered_parameters
@@ -42,15 +52,24 @@ module ActionDispatch
protected
def parameter_filter
- parameter_filter_for(@env["action_dispatch.parameter_filter"])
+ parameter_filter_for @env.fetch("action_dispatch.parameter_filter") {
+ return NULL_PARAM_FILTER
+ }
end
def env_filter
- parameter_filter_for(Array(@env["action_dispatch.parameter_filter"]) + [/RAW_POST_DATA/, "rack.request.form_vars"])
+ user_key = @env.fetch("action_dispatch.parameter_filter") {
+ return NULL_ENV_FILTER
+ }
+ parameter_filter_for(Array(user_key) + ENV_MATCH)
end
def parameter_filter_for(filters)
- @@parameter_filter_for[filters] ||= ParameterFilter.new(filters)
+ @@parameter_filter_for.synchronize do
+ # Do we *actually* need this cache? Constructing ParameterFilters
+ # doesn't seem too expensive.
+ @@parameter_filter_for[filters] ||= ParameterFilter.new(filters)
+ end
end
KV_RE = '[^&;=]+'
diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb
index a3bb25f75a..dc04d4577b 100644
--- a/actionpack/lib/action_dispatch/http/headers.rb
+++ b/actionpack/lib/action_dispatch/http/headers.rb
@@ -1,32 +1,39 @@
module ActionDispatch
module Http
- class Headers < ::Hash
- @@env_cache = Hash.new { |h,k| h[k] = "HTTP_#{k.upcase.gsub(/-/, '_')}" }
+ class Headers
+ include Enumerable
- def initialize(*args)
-
- if args.size == 1 && args[0].is_a?(Hash)
- super()
- update(args[0])
- else
- super
- end
+ def initialize(env = {})
+ @headers = env
end
def [](header_name)
- super env_name(header_name)
+ @headers[env_name(header_name)]
+ end
+
+ def []=(k,v); @headers[k] = v; end
+ def key?(k); @headers.key? k; end
+ alias :include? :key?
+
+ def fetch(header_name, *args, &block)
+ @headers.fetch env_name(header_name), *args, &block
end
- def fetch(header_name, default=nil, &block)
- super env_name(header_name), default, &block
+ def each(&block)
+ @headers.each(&block)
end
private
- # Converts a HTTP header name to an environment variable name if it is
- # not contained within the headers hash.
- def env_name(header_name)
- include?(header_name) ? header_name : @@env_cache[header_name]
- end
+
+ # Converts a HTTP header name to an environment variable name if it is
+ # not contained within the headers hash.
+ def env_name(header_name)
+ @headers.include?(header_name) ? header_name : cgi_name(header_name)
+ end
+
+ def cgi_name(k)
+ "HTTP_#{k.upcase.gsub(/-/, '_')}"
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/parameter_filter.rb b/actionpack/lib/action_dispatch/http/parameter_filter.rb
index 490b46c990..b655a54865 100644
--- a/actionpack/lib/action_dispatch/http/parameter_filter.rb
+++ b/actionpack/lib/action_dispatch/http/parameter_filter.rb
@@ -1,74 +1,72 @@
module ActionDispatch
module Http
class ParameterFilter
+ FILTERED = '[FILTERED]'.freeze # :nodoc:
- def initialize(filters)
+ def initialize(filters = [])
@filters = filters
end
def filter(params)
- if enabled?
- compiled_filter.call(params)
- else
- params.dup
- end
+ compiled_filter.call(params)
end
private
- def enabled?
- @filters.present?
+ def compiled_filter
+ @compiled_filter ||= CompiledFilter.compile(@filters)
end
- FILTERED = '[FILTERED]'.freeze
+ class CompiledFilter # :nodoc:
+ def self.compile(filters)
+ return lambda { |params| params.dup } if filters.empty?
- def compiled_filter
- @compiled_filter ||= begin
- regexps, blocks = compile_filter
+ strings, regexps, blocks = [], [], []
- lambda do |original_params|
- filtered_params = {}
+ filters.each do |item|
+ case item
+ when Proc
+ blocks << item
+ when Regexp
+ regexps << item
+ else
+ strings << item.to_s
+ end
+ end
- original_params.each do |key, value|
- if regexps.find { |r| key =~ r }
- value = FILTERED
- elsif value.is_a?(Hash)
- value = filter(value)
- elsif value.is_a?(Array)
- value = value.map { |v| v.is_a?(Hash) ? filter(v) : v }
- elsif blocks.present?
- key = key.dup
- value = value.dup if value.duplicable?
- blocks.each { |b| b.call(key, value) }
- end
+ regexps << Regexp.new(strings.join('|'), true) unless strings.empty?
+ new regexps, blocks
+ end
- filtered_params[key] = value
- end
+ attr_reader :regexps, :blocks
- filtered_params
- end
+ def initialize(regexps, blocks)
+ @regexps = regexps
+ @blocks = blocks
end
- end
- def compile_filter
- strings, regexps, blocks = [], [], []
+ def call(original_params)
+ filtered_params = {}
+
+ original_params.each do |key, value|
+ if regexps.any? { |r| key =~ r }
+ value = FILTERED
+ elsif value.is_a?(Hash)
+ value = call(value)
+ elsif value.is_a?(Array)
+ value = value.map { |v| v.is_a?(Hash) ? call(v) : v }
+ elsif blocks.any?
+ key = key.dup
+ value = value.dup if value.duplicable?
+ blocks.each { |b| b.call(key, value) }
+ end
- @filters.each do |item|
- case item
- when NilClass
- when Proc
- blocks << item
- when Regexp
- regexps << item
- else
- strings << item.to_s
+ filtered_params[key] = value
end
- end
- regexps << Regexp.new(strings.join('|'), true) unless strings.empty?
- [regexps, blocks]
+ filtered_params
+ end
end
-
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index b8ebeb408f..fc8825d6d9 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -70,7 +70,13 @@ module ActionDispatch
RFC5789 = %w(PATCH)
HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789
- HTTP_METHOD_LOOKUP = Hash.new { |h, m| h[m] = m.underscore.to_sym if HTTP_METHODS.include?(m) }
+
+ HTTP_METHOD_LOOKUP = {}
+
+ # Populate the HTTP method lookup cache
+ HTTP_METHODS.each { |method|
+ HTTP_METHOD_LOOKUP[method] = method.underscore.to_sym
+ }
# Returns the HTTP \method that the application should see.
# In the case where the \method was overridden by a middleware
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index ab740a0190..0de10695e0 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -6,7 +6,7 @@ module ActionDispatch
# and calls an exceptions app that will wrap it in a format for the end user.
#
# The exceptions app should be passed as parameter on initialization
- # of ShowExceptions. Everytime there is an exception, ShowExceptions will
+ # of ShowExceptions. Every time there is an exception, ShowExceptions will
# store the exception in env["action_dispatch.exception"], rewrite the
# PATH_INFO to the exception status code and call the rack app.
#
@@ -15,11 +15,11 @@ module ActionDispatch
# If any exception happens inside the exceptions app, this middleware
# catches the exceptions and returns a FAILSAFE_RESPONSE.
class ShowExceptions
- FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'},
- ["<html><body><h1>500 Internal Server Error</h1>" <<
+ FAILSAFE_RESPONSE = [500, { 'Content-Type' => 'text/plain' },
+ ["500 Internal Server Error\n" <<
"If you are the administrator of this website, then please read this web " <<
"application's log file and/or the web server's log file to find out what " <<
- "went wrong.</body></html>"]]
+ "went wrong."]]
def initialize(app, exceptions_app)
@app = app
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index 9073e6582d..e3b15b43b9 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -29,7 +29,7 @@ module ActionDispatch
def ext
@ext ||= begin
- ext = ::ActionController::Base.page_cache_extension
+ ext = ::ActionController::Base.default_static_extension
"{,#{ext},/index#{ext}}"
end
end
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index ccc0435a39..284dd180db 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -12,12 +12,7 @@ module ActionDispatch
config.action_dispatch.rescue_templates = { }
config.action_dispatch.rescue_responses = { }
config.action_dispatch.default_charset = nil
-
- config.action_dispatch.rack_cache = {
- :metastore => "rails:/",
- :entitystore => "rails:/",
- :verbose => false
- }
+ config.action_dispatch.rack_cache = false
config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN',
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 49afa01d25..c5cf413c8f 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1038,7 +1038,7 @@ module ActionDispatch
# === Options
# Takes same options as +resources+.
def resource(*resources, &block)
- options = resources.extract_options!
+ options = resources.extract_options!.dup
if apply_common_behavior_for(:resource, resources, options, &block)
return self
@@ -1204,7 +1204,7 @@ module ActionDispatch
# # resource actions are at /admin/posts.
# resources :posts, :path => "admin/posts"
def resources(*resources, &block)
- options = resources.extract_options!
+ options = resources.extract_options!.dup
if apply_common_behavior_for(:resources, resources, options, &block)
return self
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 091b0d8cd2..8bbf52382a 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -29,7 +29,6 @@ module ActionView
extend ActiveSupport::Autoload
eager_autoload do
- autoload :AssetPaths
autoload :Base
autoload :Context
autoload :CompiledTemplates, "action_view/context"
diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb
deleted file mode 100644
index 4bbb31b3ee..0000000000
--- a/actionpack/lib/action_view/asset_paths.rb
+++ /dev/null
@@ -1,143 +0,0 @@
-require 'zlib'
-require 'active_support/core_ext/file'
-
-module ActionView
- class AssetPaths #:nodoc:
- URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
-
- attr_reader :config, :controller
-
- def initialize(config, controller = nil)
- @config = config
- @controller = controller
- end
-
- # Add the extension +ext+ if not present. Return full or scheme-relative URLs otherwise untouched.
- # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
- # roots. Rewrite the asset path for cache-busting asset ids. Include
- # asset host, if configured, with the correct request protocol.
- #
- # When :relative (default), the protocol will be determined by the client using current protocol
- # When :request, the protocol will be the request protocol
- # Otherwise, the protocol is used (E.g. :http, :https, etc)
- def compute_public_path(source, dir, options = {})
- source = source.to_s
- return source if is_uri?(source)
-
- source = rewrite_extension(source, dir, options[:ext]) if options[:ext]
- source = rewrite_asset_path(source, dir, options)
- source = rewrite_relative_url_root(source, relative_url_root)
- source = rewrite_host_and_protocol(source, options[:protocol])
- source
- end
-
- # Return the filesystem path for the source
- def compute_source_path(source, dir, ext)
- source = rewrite_extension(source, dir, ext) if ext
-
- sources = []
- sources << config.assets_dir
- sources << dir unless source[0] == ?/
- sources << source
-
- File.join(sources)
- end
-
- def is_uri?(path)
- path =~ URI_REGEXP
- end
-
- private
-
- def rewrite_extension(source, dir, ext)
- raise NotImplementedError
- end
-
- def rewrite_asset_path(source, path = nil)
- raise NotImplementedError
- end
-
- def rewrite_relative_url_root(source, relative_url_root)
- relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source
- end
-
- def has_request?
- controller.respond_to?(:request)
- end
-
- def rewrite_host_and_protocol(source, protocol = nil)
- host = compute_asset_host(source)
- if host && !is_uri?(host)
- if (protocol || default_protocol) == :request && !has_request?
- host = nil
- else
- host = "#{compute_protocol(protocol)}#{host}"
- end
- end
- host ? "#{host}#{source}" : source
- end
-
- def compute_protocol(protocol)
- protocol ||= default_protocol
- case protocol
- when :relative
- "//"
- when :request
- unless @controller
- invalid_asset_host!("The protocol requested was :request. Consider using :relative instead.")
- end
- @controller.request.protocol
- else
- "#{protocol}://"
- end
- end
-
- def default_protocol
- @config.default_asset_host_protocol || (has_request? ? :request : :relative)
- end
-
- def invalid_asset_host!(help_message)
- raise ActionView::MissingRequestError, "This asset host cannot be computed without a request in scope. #{help_message}"
- end
-
- # Pick an asset host for this source. Returns +nil+ if no host is set,
- # the host if no wildcard is set, the host interpolated with the
- # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
- # or the value returned from invoking call on an object responding to call
- # (proc or otherwise).
- def compute_asset_host(source)
- if host = asset_host_config
- if host.respond_to?(:call)
- args = [source]
- arity = arity_of(host)
- if (arity > 1 || arity < -2) && !has_request?
- invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request, or make it optional.")
- end
- args << current_request if (arity > 1 || arity < 0) && has_request?
- host.call(*args)
- else
- (host =~ /%d/) ? host % (Zlib.crc32(source) % 4) : host
- end
- end
- end
-
- def relative_url_root
- config.relative_url_root || current_request.try(:script_name)
- end
-
- def asset_host_config
- config.asset_host
- end
-
- # Returns the current request if one exists.
- def current_request
- controller.request if has_request?
- end
-
- # Returns the arity of a callable
- def arity_of(callable)
- callable.respond_to?(:arity) ? callable.arity : callable.method(:call).arity
- end
-
- end
-end
diff --git a/actionpack/lib/action_view/digestor.rb b/actionpack/lib/action_view/digestor.rb
index 5d3add4091..1c6eaf36f7 100644
--- a/actionpack/lib/action_view/digestor.rb
+++ b/actionpack/lib/action_view/digestor.rb
@@ -1,3 +1,5 @@
+require 'mutex_m'
+
module ActionView
class Digestor
EXPLICIT_DEPENDENCY = /# Template Dependency: ([^ ]+)/
@@ -19,16 +21,30 @@ module ActionView
/x
cattr_reader(:cache)
- @@cache = Hash.new
+ @@cache = Hash.new.extend Mutex_m
def self.digest(name, format, finder, options = {})
- cache["#{name}.#{format}"] ||= new(name, format, finder, options).digest
+ cache.synchronize do
+ unsafe_digest name, format, finder, options
+ end
end
- attr_reader :name, :format, :finder, :options
+ ###
+ # This method is NOT thread safe. DO NOT CALL IT DIRECTLY, instead call
+ # Digestor.digest
+ def self.unsafe_digest(name, format, finder, options = {}) # :nodoc:
+ key = "#{name}.#{format}"
- def initialize(name, format, finder, options = {})
- @name, @format, @finder, @options = name, format, finder, options
+ cache.fetch(key) do
+ klass = options[:partial] || name.include?("/_") ? PartialDigestor : Digestor
+ cache[key] = klass.new(name, format, finder).digest
+ end
+ end
+
+ attr_reader :name, :format, :finder
+
+ def initialize(name, format, finder)
+ @name, @format, @finder = name, format, finder
end
def digest
@@ -48,7 +64,7 @@ module ActionView
def nested_dependencies
dependencies.collect do |dependency|
- dependencies = Digestor.new(dependency, format, finder, partial: true).nested_dependencies
+ dependencies = PartialDigestor.new(dependency, format, finder).nested_dependencies
dependencies.any? ? { dependency => dependencies } : dependency
end
end
@@ -64,11 +80,11 @@ module ActionView
end
def directory
- name.split("/").first
+ name.split("/")[0..-2].join("/")
end
def partial?
- options[:partial] || name.include?("/_")
+ false
end
def source
@@ -77,7 +93,7 @@ module ActionView
def dependency_digest
dependencies.collect do |template_name|
- Digestor.digest(template_name, format, finder, partial: true)
+ Digestor.unsafe_digest(template_name, format, finder, partial: true)
end.join("-")
end
@@ -101,4 +117,10 @@ module ActionView
source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
end
end
+
+ class PartialDigestor < Digestor # :nodoc:
+ def partial?
+ true
+ end
+ end
end
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
index f2a3a494bc..269e78a021 100644
--- a/actionpack/lib/action_view/helpers.rb
+++ b/actionpack/lib/action_view/helpers.rb
@@ -4,6 +4,7 @@ module ActionView #:nodoc:
autoload :ActiveModelHelper
autoload :AssetTagHelper
+ autoload :AssetUrlHelper
autoload :AtomFeedHelper
autoload :BenchmarkHelper
autoload :CacheHelper
@@ -28,12 +29,9 @@ module ActionView #:nodoc:
extend ActiveSupport::Concern
- included do
- extend SanitizeHelper::ClassMethods
- end
-
include ActiveModelHelper
include AssetTagHelper
+ include AssetUrlHelper
include AtomFeedHelper
include BenchmarkHelper
include CacheHelper
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 5b5fc84e90..4eac6514df 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -1,8 +1,6 @@
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/hash/keys'
-require 'action_view/helpers/asset_tag_helpers/javascript_tag_helpers'
-require 'action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers'
-require 'action_view/helpers/asset_tag_helpers/asset_paths'
+require 'action_view/helpers/asset_url_helper'
require 'action_view/helpers/tag_helper'
module ActionView
@@ -17,187 +15,87 @@ module ActionView
# stylesheet_link_tag("application")
# # => <link href="/assets/application.css?body=1" media="screen" rel="stylesheet" />
#
- #
- # === Using asset hosts
- #
- # By default, Rails links to these assets on the current host in the public
- # folder, but you can direct Rails to link to assets from a dedicated asset
- # server by setting <tt>ActionController::Base.asset_host</tt> in the application
- # configuration, typically in <tt>config/environments/production.rb</tt>.
- # For example, you'd define <tt>assets.example.com</tt> to be your asset
- # host this way, inside the <tt>configure</tt> block of your environment-specific
- # configuration files or <tt>config/application.rb</tt>:
- #
- # config.action_controller.asset_host = "assets.example.com"
- #
- # Helpers take that into account:
- #
- # image_tag("rails.png")
- # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
- # stylesheet_link_tag("application")
- # # => <link href="http://assets.example.com/assets/application.css" media="screen" rel="stylesheet" />
- #
- # Browsers typically open at most two simultaneous connections to a single
- # host, which means your assets often have to wait for other assets to finish
- # downloading. You can alleviate this by using a <tt>%d</tt> wildcard in the
- # +asset_host+. For example, "assets%d.example.com". If that wildcard is
- # present Rails distributes asset requests among the corresponding four hosts
- # "assets0.example.com", ..., "assets3.example.com". With this trick browsers
- # will open eight simultaneous connections rather than two.
- #
- # image_tag("rails.png")
- # # => <img alt="Rails" src="http://assets0.example.com/assets/rails.png" />
- # stylesheet_link_tag("application")
- # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
- #
- # To do this, you can either setup four actual hosts, or you can use wildcard
- # DNS to CNAME the wildcard to a single asset host. You can read more about
- # setting up your DNS CNAME records from your ISP.
- #
- # Note: This is purely a browser performance optimization and is not meant
- # for server load balancing. See http://www.die.net/musings/page_load_time/
- # for background.
- #
- # Alternatively, you can exert more control over the asset host by setting
- # +asset_host+ to a proc like this:
- #
- # ActionController::Base.asset_host = Proc.new { |source|
- # "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com"
- # }
- # image_tag("rails.png")
- # # => <img alt="Rails" src="http://assets1.example.com/assets/rails.png" />
- # stylesheet_link_tag("application")
- # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
- #
- # The example above generates "http://assets1.example.com" and
- # "http://assets2.example.com". This option is useful for example if
- # you need fewer/more than four hosts, custom host names, etc.
- #
- # As you see the proc takes a +source+ parameter. That's a string with the
- # absolute path of the asset, for example "/assets/rails.png".
- #
- # ActionController::Base.asset_host = Proc.new { |source|
- # if source.ends_with?('.css')
- # "http://stylesheets.example.com"
- # else
- # "http://assets.example.com"
- # end
- # }
- # image_tag("rails.png")
- # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
- # stylesheet_link_tag("application")
- # # => <link href="http://stylesheets.example.com/assets/application.css" media="screen" rel="stylesheet" />
- #
- # Alternatively you may ask for a second parameter +request+. That one is
- # particularly useful for serving assets from an SSL-protected page. The
- # example proc below disables asset hosting for HTTPS connections, while
- # still sending assets for plain HTTP requests from asset hosts. If you don't
- # have SSL certificates for each of the asset hosts this technique allows you
- # to avoid warnings in the client about mixed media.
- #
- # config.action_controller.asset_host = Proc.new { |source, request|
- # if request.ssl?
- # "#{request.protocol}#{request.host_with_port}"
- # else
- # "#{request.protocol}assets.example.com"
- # end
- # }
- #
- # You can also implement a custom asset host object that responds to +call+
- # and takes either one or two parameters just like the proc.
- #
- # config.action_controller.asset_host = AssetHostingWithMinimumSsl.new(
- # "http://asset%d.example.com", "https://asset1.example.com"
- # )
- #
- # === Customizing the asset path
- #
- # By default, Rails appends asset's timestamps to all asset paths. This allows
- # you to set a cache-expiration date for the asset far into the future, but
- # still be able to instantly invalidate it by simply updating the file (and
- # hence updating the timestamp, which then updates the URL as the timestamp
- # is part of that, which in turn busts the cache).
- #
- # It's the responsibility of the web server you use to set the far-future
- # expiration date on cache assets that you need to take advantage of this
- # feature. Here's an example for Apache:
- #
- # # Asset Expiration
- # ExpiresActive On
- # <FilesMatch "\.(ico|gif|jpe?g|png|js|css)$">
- # ExpiresDefault "access plus 1 year"
- # </FilesMatch>
- #
- # Also note that in order for this to work, all your application servers must
- # return the same timestamps. This means that they must have their clocks
- # synchronized. If one of them drifts out of sync, you'll see different
- # timestamps at random and the cache won't work. In that case the browser
- # will request the same assets over and over again even thought they didn't
- # change. You can use something like Live HTTP Headers for Firefox to verify
- # that the cache is indeed working.
- #
- # This strategy works well enough for most server setups and requires the
- # least configuration, but if you deploy several application servers at
- # different times - say to handle a temporary spike in load - then the
- # asset time stamps will be out of sync. In a setup like this you may want
- # to set the way that asset paths are generated yourself.
- #
- # Altering the asset paths that Rails generates can be done in two ways.
- # The easiest is to define the RAILS_ASSET_ID environment variable. The
- # contents of this variable will always be used in preference to
- # calculated timestamps. A more complex but flexible way is to set
- # <tt>ActionController::Base.config.asset_path</tt> to a proc
- # that takes the unmodified asset path and returns the path needed for
- # your asset caching to work. Typically you'd do something like this in
- # <tt>config/environments/production.rb</tt>:
- #
- # # Normally you'd calculate RELEASE_NUMBER at startup.
- # RELEASE_NUMBER = 12345
- # config.action_controller.asset_path = proc { |asset_path|
- # "/release-#{RELEASE_NUMBER}#{asset_path}"
- # }
- #
- # This example would cause the following behavior on all servers no
- # matter when they were deployed:
- #
- # image_tag("rails.png")
- # # => <img alt="Rails" src="/release-12345/images/rails.png" />
- # stylesheet_link_tag("application")
- # # => <link href="/release-12345/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" />
- #
- # Changing the asset_path does require that your web servers have
- # knowledge of the asset template paths that you rewrite to so it's not
- # suitable for out-of-the-box use. To use the example given above you
- # could use something like this in your Apache VirtualHost configuration:
- #
- # <LocationMatch "^/release-\d+/(images|javascripts|stylesheets)/.*$">
- # # Some browsers still send conditional-GET requests if there's a
- # # Last-Modified header or an ETag header even if they haven't
- # # reached the expiry date sent in the Expires header.
- # Header unset Last-Modified
- # Header unset ETag
- # FileETag None
- #
- # # Assets requested using a cache-busting filename should be served
- # # only once and then cached for a really long time. The HTTP/1.1
- # # spec frowns on hugely-long expiration times though and suggests
- # # that assets which never expire be served with an expiration date
- # # 1 year from access.
- # ExpiresActive On
- # ExpiresDefault "access plus 1 year"
- # </LocationMatch>
- #
- # # We use cached-busting location names with the far-future expires
- # # headers to ensure that if a file does change it can force a new
- # # request. The actual asset filenames are still the same though so we
- # # need to rewrite the location from the cache-busting location to the
- # # real asset location so that we can serve it.
- # RewriteEngine On
- # RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L]
module AssetTagHelper
+ extend ActiveSupport::Concern
+
+ include AssetUrlHelper
include TagHelper
- include JavascriptTagHelpers
- include StylesheetTagHelpers
+
+ # Returns an HTML script tag for each of the +sources+ provided.
+ #
+ # Sources may be paths to JavaScript files. Relative paths are assumed to be relative
+ # to <tt>public/javascripts</tt>, full paths are assumed to be relative to the document
+ # root. Relative paths are idiomatic, use absolute paths only when needed.
+ #
+ # When passing paths, the ".js" extension is optional.
+ #
+ # You can modify the HTML attributes of the script tag by passing a hash as the
+ # last argument.
+ #
+ # javascript_include_tag "xmlhr"
+ # # => <script src="/javascripts/xmlhr.js?1284139606"></script>
+ #
+ # javascript_include_tag "xmlhr.js"
+ # # => <script src="/javascripts/xmlhr.js?1284139606"></script>
+ #
+ # javascript_include_tag "common.javascript", "/elsewhere/cools"
+ # # => <script src="/javascripts/common.javascript?1284139606"></script>
+ # # <script src="/elsewhere/cools.js?1423139606"></script>
+ #
+ # javascript_include_tag "http://www.example.com/xmlhr"
+ # # => <script src="http://www.example.com/xmlhr"></script>
+ #
+ # javascript_include_tag "http://www.example.com/xmlhr.js"
+ # # => <script src="http://www.example.com/xmlhr.js"></script>
+ #
+ def javascript_include_tag(*sources)
+ options = sources.extract_options!.stringify_keys
+ sources.uniq.map { |source|
+ tag_options = {
+ "src" => path_to_javascript(source)
+ }.merge(options)
+ content_tag(:script, "", tag_options)
+ }.join("\n").html_safe
+ end
+
+ # Returns a stylesheet link tag for the sources specified as arguments. If
+ # you don't specify an extension, <tt>.css</tt> will be appended automatically.
+ # You can modify the link attributes by passing a hash as the last argument.
+ # For historical reasons, the 'media' attribute will always be present and defaults
+ # to "screen", so you must explicitely set it to "all" for the stylesheet(s) to
+ # apply to all media types.
+ #
+ # stylesheet_link_tag "style" # =>
+ # <link href="/stylesheets/style.css" media="screen" rel="stylesheet" />
+ #
+ # stylesheet_link_tag "style.css" # =>
+ # <link href="/stylesheets/style.css" media="screen" rel="stylesheet" />
+ #
+ # stylesheet_link_tag "http://www.example.com/style.css" # =>
+ # <link href="http://www.example.com/style.css" media="screen" rel="stylesheet" />
+ #
+ # stylesheet_link_tag "style", :media => "all" # =>
+ # <link href="/stylesheets/style.css" media="all" rel="stylesheet" />
+ #
+ # stylesheet_link_tag "style", :media => "print" # =>
+ # <link href="/stylesheets/style.css" media="print" rel="stylesheet" />
+ #
+ # stylesheet_link_tag "random.styles", "/css/stylish" # =>
+ # <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" />
+ # <link href="/css/stylish.css" media="screen" rel="stylesheet" />
+ #
+ def stylesheet_link_tag(*sources)
+ options = sources.extract_options!.stringify_keys
+ sources.uniq.map { |source|
+ tag_options = {
+ "rel" => "stylesheet",
+ "media" => "screen",
+ "href" => path_to_stylesheet(source)
+ }.merge(options)
+ tag(:link, tag_options)
+ }.join("\n").html_safe
+ end
+
# Returns a link tag that browsers and news readers can use to auto-detect
# an RSS or Atom feed. The +type+ can either be <tt>:rss</tt> (default) or
# <tt>:atom</tt>. Control the link options in url_for format using the
@@ -268,93 +166,6 @@ module ActionView
}.merge(options.symbolize_keys))
end
- # Computes the path to an image asset.
- # Full paths from the document root will be passed through.
- # Used internally by +image_tag+ to build the image path:
- #
- # image_path("edit") # => "/assets/edit"
- # image_path("edit.png") # => "/assets/edit.png"
- # image_path("icons/edit.png") # => "/assets/icons/edit.png"
- # image_path("/icons/edit.png") # => "/icons/edit.png"
- # image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
- #
- # If you have images as application resources this method may conflict with their named routes.
- # The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
- # plugin authors are encouraged to do so.
- def image_path(source)
- source.present? ? asset_paths.compute_public_path(source, 'images') : ""
- end
- alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
-
- # Computes the full URL to an image asset.
- # This will use +image_path+ internally, so most of their behaviors will be the same.
- def image_url(source)
- URI.join(current_host, path_to_image(source)).to_s
- end
- alias_method :url_to_image, :image_url # aliased to avoid conflicts with an image_url named route
-
- # Computes the path to a video asset in the public videos directory.
- # Full paths from the document root will be passed through.
- # Used internally by +video_tag+ to build the video path.
- #
- # video_path("hd") # => /videos/hd
- # video_path("hd.avi") # => /videos/hd.avi
- # video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
- # video_path("/trailers/hd.avi") # => /trailers/hd.avi
- # video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi
- def video_path(source)
- asset_paths.compute_public_path(source, 'videos')
- end
- alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
-
- # Computes the full URL to a video asset in the public videos directory.
- # This will use +video_path+ internally, so most of their behaviors will be the same.
- def video_url(source)
- URI.join(current_host, path_to_video(source)).to_s
- end
- alias_method :url_to_video, :video_url # aliased to avoid conflicts with an video_url named route
-
- # Computes the path to an audio asset in the public audios directory.
- # Full paths from the document root will be passed through.
- # Used internally by +audio_tag+ to build the audio path.
- #
- # audio_path("horse") # => /audios/horse
- # audio_path("horse.wav") # => /audios/horse.wav
- # audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav
- # audio_path("/sounds/horse.wav") # => /sounds/horse.wav
- # audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav
- def audio_path(source)
- asset_paths.compute_public_path(source, 'audios')
- end
- alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
-
- # Computes the full URL to an audio asset in the public audios directory.
- # This will use +audio_path+ internally, so most of their behaviors will be the same.
- def audio_url(source)
- URI.join(current_host, path_to_audio(source)).to_s
- end
- alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
-
- # Computes the path to a font asset.
- # Full paths from the document root will be passed through.
- #
- # font_path("font") # => /assets/font
- # font_path("font.ttf") # => /assets/font.ttf
- # font_path("dir/font.ttf") # => /assets/dir/font.ttf
- # font_path("/dir/font.ttf") # => /dir/font.ttf
- # font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
- def font_path(source)
- asset_paths.compute_public_path(source, 'fonts')
- end
- alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
-
- # Computes the full URL to a font asset.
- # This will use +font_path+ internally, so most of their behaviors will be the same.
- def font_url(source)
- URI.join(current_host, path_to_font(source)).to_s
- end
- alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route
-
# Returns an html image tag for the +source+. The +source+ can be a full
# path or a file.
#
@@ -462,11 +273,6 @@ module ActionView
end
private
-
- def asset_paths
- @asset_paths ||= AssetTagHelper::AssetPaths.new(config, controller)
- end
-
def multiple_sources_tag(type, sources)
options = sources.extract_options!.symbolize_keys
sources.flatten!
@@ -482,10 +288,6 @@ module ActionView
content_tag(type, nil, options)
end
end
-
- def current_host
- url_for(:only_path => false)
- end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
deleted file mode 100644
index e42e49fb04..0000000000
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
+++ /dev/null
@@ -1,145 +0,0 @@
-require 'active_support/core_ext/string/inflections'
-require 'active_support/core_ext/file'
-require 'action_view/helpers/tag_helper'
-
-module ActionView
- module Helpers
- module AssetTagHelper
-
- class AssetIncludeTag #:nodoc:
- include TagHelper
-
- attr_reader :config, :asset_paths
- class_attribute :expansions
-
- def self.inherited(base)
- base.expansions = { }
- end
-
- def initialize(config, asset_paths)
- @config = config
- @asset_paths = asset_paths
- end
-
- def asset_name
- raise NotImplementedError
- end
-
- def extension
- raise NotImplementedError
- end
-
- def custom_dir
- raise NotImplementedError
- end
-
- def asset_tag(source, options)
- raise NotImplementedError
- end
-
- def include_tag(*sources)
- options = sources.extract_options!.stringify_keys
- concat = options.delete("concat")
- cache = concat || options.delete("cache")
- recursive = options.delete("recursive")
-
- if concat || (config.perform_caching && cache)
- joined_name = (cache == true ? "all" : cache) + ".#{extension}"
- joined_path = File.join((joined_name[/^#{File::SEPARATOR}/] ? config.assets_dir : custom_dir), joined_name)
- unless config.perform_caching && File.exists?(joined_path)
- write_asset_file_contents(joined_path, compute_paths(sources, recursive))
- end
- asset_tag(joined_name, options)
- else
- sources = expand_sources(sources, recursive)
- ensure_sources!(sources) if cache
- sources.collect { |source| asset_tag(source, options) }.join("\n").html_safe
- end
- end
-
- private
-
- def path_to_asset(source, options = {})
- asset_paths.compute_public_path(source, asset_name.to_s.pluralize, options.merge(:ext => extension))
- end
-
- def path_to_asset_source(source)
- asset_paths.compute_source_path(source, asset_name.to_s.pluralize, extension)
- end
-
- def compute_paths(*args)
- expand_sources(*args).collect { |source| path_to_asset_source(source) }
- end
-
- def expand_sources(sources, recursive)
- if sources.first == :all
- collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}")
- else
- sources.inject([]) do |list, source|
- determined_source = determine_source(source, expansions)
- update_source_list(list, determined_source)
- end
- end
- end
-
- def update_source_list(list, source)
- case source
- when String
- list.delete(source)
- list << source
- when Array
- updated_sources = source - list
- list.concat(updated_sources)
- end
- end
-
- def ensure_sources!(sources)
- sources.each do |source|
- asset_file_path!(path_to_asset_source(source))
- end
- end
-
- def collect_asset_files(*path)
- dir = path.first
-
- Dir[File.join(*path.compact)].collect do |file|
- file[-(file.size - dir.size - 1)..-1].sub(/\.\w+$/, '')
- end.sort
- end
-
- def determine_source(source, collection)
- case source
- when Symbol
- collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
- else
- source
- end
- end
-
- def join_asset_file_contents(paths)
- paths.collect { |path| File.read(asset_file_path!(path, true)) }.join("\n\n")
- end
-
- def write_asset_file_contents(joined_asset_path, asset_paths)
- FileUtils.mkdir_p(File.dirname(joined_asset_path))
- File.atomic_write(joined_asset_path) { |cache| cache.write(join_asset_file_contents(asset_paths)) }
-
- # Set mtime to the latest of the combined files to allow for
- # consistent ETag without a shared filesystem.
- mt = asset_paths.map { |p| File.mtime(asset_file_path!(p)) }.max
- File.utime(mt, mt, joined_asset_path)
- end
-
- def asset_file_path!(absolute_path, error_if_file_is_uri = false)
- if asset_paths.is_uri?(absolute_path)
- raise(Errno::ENOENT, "Asset file #{path} is uri and cannot be merged into single file") if error_if_file_is_uri
- else
- raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path)
- return absolute_path
- end
- end
- end
-
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
deleted file mode 100644
index 35f91cec18..0000000000
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-require 'thread'
-require 'active_support/core_ext/file'
-require 'active_support/core_ext/module/attribute_accessors'
-
-module ActionView
- module Helpers
- module AssetTagHelper
-
- class AssetPaths < ::ActionView::AssetPaths #:nodoc:
- # You can enable or disable the asset tag ids cache.
- # With the cache enabled, the asset tag helper methods will make fewer
- # expensive file system calls (the default implementation checks the file
- # system timestamp). However this prevents you from modifying any asset
- # files while the server is running.
- #
- # ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
- mattr_accessor :cache_asset_ids
-
- # Add or change an asset id in the asset id cache. This can be used
- # for SASS on Heroku.
- # :api: public
- def add_to_asset_ids_cache(source, asset_id)
- self.asset_ids_cache_guard.synchronize do
- self.asset_ids_cache[source] = asset_id
- end
- end
-
- private
-
- def rewrite_extension(source, dir, ext)
- source_ext = File.extname(source)
-
- source_with_ext = if source_ext.empty?
- "#{source}.#{ext}"
- elsif ext != source_ext[1..-1]
- with_ext = "#{source}.#{ext}"
- with_ext if File.exist?(File.join(config.assets_dir, dir, with_ext))
- end
-
- source_with_ext || source
- end
-
- # Break out the asset path rewrite in case plugins wish to put the asset id
- # someplace other than the query string.
- def rewrite_asset_path(source, dir, options = nil)
- source = "/#{dir}/#{source}" unless source[0] == ?/
- path = config.asset_path
-
- if path && path.respond_to?(:call)
- return path.call(source)
- elsif path && path.is_a?(String)
- return path % [source]
- end
-
- asset_id = rails_asset_id(source)
- if asset_id.empty?
- source
- else
- "#{source}?#{asset_id}"
- end
- end
-
- mattr_accessor :asset_ids_cache
- self.asset_ids_cache = {}
-
- mattr_accessor :asset_ids_cache_guard
- self.asset_ids_cache_guard = Mutex.new
-
- # Use the RAILS_ASSET_ID environment variable or the source's
- # modification time as its cache-busting asset id.
- def rails_asset_id(source)
- if asset_id = ENV["RAILS_ASSET_ID"]
- asset_id
- else
- if self.cache_asset_ids && (asset_id = self.asset_ids_cache[source])
- asset_id
- else
- path = File.join(config.assets_dir, source)
- asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : ''
-
- if self.cache_asset_ids
- add_to_asset_ids_cache(source, asset_id)
- end
-
- asset_id
- end
- end
- end
- end
-
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
deleted file mode 100644
index 139f4d19ab..0000000000
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
+++ /dev/null
@@ -1,195 +0,0 @@
-require 'active_support/core_ext/file'
-require 'action_view/helpers/asset_tag_helpers/asset_include_tag'
-
-module ActionView
- module Helpers
- module AssetTagHelper
-
- class JavascriptIncludeTag < AssetIncludeTag #:nodoc:
- def asset_name
- 'javascript'
- end
-
- def extension
- 'js'
- end
-
- def asset_tag(source, options)
- content_tag("script", "", { "src" => path_to_asset(source) }.merge(options))
- end
-
- def custom_dir
- config.javascripts_dir
- end
-
- private
-
- def expand_sources(sources, recursive = false)
- if sources.include?(:all)
- all_asset_files = (collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}") - ['application'])
- add_application_js(all_asset_files, sources)
- ((determine_source(:defaults, expansions).dup & all_asset_files) + all_asset_files).uniq
- else
- expanded_sources = sources.inject([]) do |list, source|
- determined_source = determine_source(source, expansions)
- update_source_list(list, determined_source)
- end
- add_application_js(expanded_sources, sources)
- expanded_sources
- end
- end
-
- def add_application_js(expanded_sources, sources)
- if (sources.include?(:defaults) || sources.include?(:all)) && File.exist?(File.join(custom_dir, "application.#{extension}"))
- expanded_sources.delete('application')
- expanded_sources << "application"
- end
- end
- end
-
-
- module JavascriptTagHelpers
- extend ActiveSupport::Concern
-
- module ClassMethods
- # Register one or more javascript files to be included when <tt>symbol</tt>
- # is passed to <tt>javascript_include_tag</tt>. This method is typically intended
- # to be called from plugin initialization to register javascript files
- # that the plugin installed in <tt>public/javascripts</tt>.
- #
- # ActionView::Helpers::AssetTagHelper.register_javascript_expansion :monkey => ["head", "body", "tail"]
- #
- # javascript_include_tag :monkey # =>
- # <script src="/javascripts/head.js"></script>
- # <script src="/javascripts/body.js"></script>
- # <script src="/javascripts/tail.js"></script>
- def register_javascript_expansion(expansions)
- js_expansions = JavascriptIncludeTag.expansions
- expansions.each do |key, values|
- js_expansions[key] = (js_expansions[key] || []) | Array(values)
- end
- end
- end
-
- # Computes the path to a javascript asset in the public javascripts directory.
- # If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
- # Full paths from the document root will be passed through.
- # Used internally by javascript_include_tag to build the script path.
- #
- # javascript_path "xmlhr" # => /javascripts/xmlhr.js
- # javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
- # javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
- # javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr
- # javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
- def javascript_path(source)
- asset_paths.compute_public_path(source, 'javascripts', :ext => 'js')
- end
- alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
-
- # Computes the full URL to a javascript asset in the public javascripts directory.
- # This will use +javascript_path+ internally, so most of their behaviors will be the same.
- def javascript_url(source)
- URI.join(current_host, path_to_javascript(source)).to_s
- end
- alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
-
- # Returns an HTML script tag for each of the +sources+ provided.
- #
- # Sources may be paths to JavaScript files. Relative paths are assumed to be relative
- # to <tt>public/javascripts</tt>, full paths are assumed to be relative to the document
- # root. Relative paths are idiomatic, use absolute paths only when needed.
- #
- # When passing paths, the ".js" extension is optional.
- #
- # If the application is not using the asset pipeline, to include the default JavaScript
- # expansion pass <tt>:defaults</tt> as source. By default, <tt>:defaults</tt> loads jQuery,
- # and that can be overridden in <tt>config/application.rb</tt>:
- #
- # config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js)
- #
- # When using <tt>:defaults</tt> or <tt>:all</tt>, if an <tt>application.js</tt> file exists
- # in <tt>public/javascripts</tt> it will be included as well at the end.
- #
- # You can modify the HTML attributes of the script tag by passing a hash as the
- # last argument.
- #
- # javascript_include_tag "xmlhr"
- # # => <script src="/javascripts/xmlhr.js?1284139606"></script>
- #
- # javascript_include_tag "xmlhr.js"
- # # => <script src="/javascripts/xmlhr.js?1284139606"></script>
- #
- # javascript_include_tag "common.javascript", "/elsewhere/cools"
- # # => <script src="/javascripts/common.javascript?1284139606"></script>
- # # <script src="/elsewhere/cools.js?1423139606"></script>
- #
- # javascript_include_tag "http://www.example.com/xmlhr"
- # # => <script src="http://www.example.com/xmlhr"></script>
- #
- # javascript_include_tag "http://www.example.com/xmlhr.js"
- # # => <script src="http://www.example.com/xmlhr.js"></script>
- #
- # javascript_include_tag :defaults
- # # => <script src="/javascripts/jquery.js?1284139606"></script>
- # # <script src="/javascripts/rails.js?1284139606"></script>
- # # <script src="/javascripts/application.js?1284139606"></script>
- #
- # Note: The application.js file is only referenced if it exists
- #
- # You can also include all JavaScripts in the +javascripts+ directory using <tt>:all</tt> as the source:
- #
- # javascript_include_tag :all
- # # => <script src="/javascripts/jquery.js?1284139606"></script>
- # # <script src="/javascripts/rails.js?1284139606"></script>
- # # <script src="/javascripts/shop.js?1284139606"></script>
- # # <script src="/javascripts/checkout.js?1284139606"></script>
- # # <script src="/javascripts/application.js?1284139606"></script>
- #
- # Note that your defaults of choice will be included first, so they will be available to all subsequently
- # included files.
- #
- # If you want Rails to search in all the subdirectories under <tt>public/javascripts</tt>, you should
- # explicitly set <tt>:recursive</tt>:
- #
- # javascript_include_tag :all, :recursive => true
- #
- # == Caching multiple JavaScripts into one
- #
- # You can also cache multiple JavaScripts into one file, which requires less HTTP connections to download
- # and can better be compressed by gzip (leading to faster transfers). Caching will only happen if
- # <tt>config.perform_caching</tt> is set to true (which is the case by default for the Rails
- # production environment, but not for the development environment).
- #
- # # assuming config.perform_caching is false
- # javascript_include_tag :all, :cache => true
- # # => <script src="/javascripts/jquery.js?1284139606"></script>
- # # <script src="/javascripts/rails.js?1284139606"></script>
- # # <script src="/javascripts/shop.js?1284139606"></script>
- # # <script src="/javascripts/checkout.js?1284139606"></script>
- # # <script src="/javascripts/application.js?1284139606"></script>
- #
- # # assuming config.perform_caching is true
- # javascript_include_tag :all, :cache => true
- # # => <script src="/javascripts/all.js?1344139789"></script>
- #
- # # assuming config.perform_caching is false
- # javascript_include_tag "jquery", "cart", "checkout", :cache => "shop"
- # # => <script src="/javascripts/jquery.js?1284139606"></script>
- # # <script src="/javascripts/cart.js?1289139157"></script>
- # # <script src="/javascripts/checkout.js?1299139816"></script>
- #
- # # assuming config.perform_caching is true
- # javascript_include_tag "jquery", "cart", "checkout", :cache => "shop"
- # # => <script src="/javascripts/shop.js?1299139816"></script>
- #
- # The <tt>:recursive</tt> option is also available for caching:
- #
- # javascript_include_tag :all, :cache => true, :recursive => true
- def javascript_include_tag(*sources)
- @javascript_include ||= JavascriptIncludeTag.new(config, asset_paths)
- @javascript_include.include_tag(*sources)
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
deleted file mode 100644
index e3a86a8889..0000000000
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
+++ /dev/null
@@ -1,151 +0,0 @@
-require 'active_support/core_ext/file'
-require 'action_view/helpers/asset_tag_helpers/asset_include_tag'
-
-module ActionView
- module Helpers
- module AssetTagHelper
-
- class StylesheetIncludeTag < AssetIncludeTag #:nodoc:
- def asset_name
- 'stylesheet'
- end
-
- def extension
- 'css'
- end
-
- def asset_tag(source, options)
- # We force the :request protocol here to avoid a double-download bug in IE7 and IE8
- tag("link", { "rel" => "stylesheet", "media" => "screen", "href" => path_to_asset(source, :protocol => :request) }.merge(options))
- end
-
- def custom_dir
- config.stylesheets_dir
- end
- end
-
-
- module StylesheetTagHelpers
- extend ActiveSupport::Concern
-
- module ClassMethods
- # Register one or more stylesheet files to be included when <tt>symbol</tt>
- # is passed to <tt>stylesheet_link_tag</tt>. This method is typically intended
- # to be called from plugin initialization to register stylesheet files
- # that the plugin installed in <tt>public/stylesheets</tt>.
- #
- # ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion :monkey => ["head", "body", "tail"]
- #
- # stylesheet_link_tag :monkey # =>
- # <link href="/stylesheets/head.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/body.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/tail.css" media="screen" rel="stylesheet" />
- def register_stylesheet_expansion(expansions)
- style_expansions = StylesheetIncludeTag.expansions
- expansions.each do |key, values|
- style_expansions[key] = (style_expansions[key] || []) | Array(values)
- end
- end
- end
-
- # Computes the path to a stylesheet asset in the public stylesheets directory.
- # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
- # Full paths from the document root will be passed through.
- # Used internally by +stylesheet_link_tag+ to build the stylesheet path.
- #
- # stylesheet_path "style" # => /stylesheets/style.css
- # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
- # stylesheet_path "/dir/style.css" # => /dir/style.css
- # stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style
- # stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css
- def stylesheet_path(source)
- asset_paths.compute_public_path(source, 'stylesheets', :ext => 'css', :protocol => :request)
- end
- alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
-
- # Computes the full URL to a stylesheet asset in the public stylesheets directory.
- # This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
- def stylesheet_url(source)
- URI.join(current_host, path_to_stylesheet(source)).to_s
- end
- alias_method :url_to_stylesheet, :stylesheet_url # aliased to avoid conflicts with a stylesheet_url named route
-
- # Returns a stylesheet link tag for the sources specified as arguments. If
- # you don't specify an extension, <tt>.css</tt> will be appended automatically.
- # You can modify the link attributes by passing a hash as the last argument.
- # For historical reasons, the 'media' attribute will always be present and defaults
- # to "screen", so you must explicitely set it to "all" for the stylesheet(s) to
- # apply to all media types.
- #
- # stylesheet_link_tag "style" # =>
- # <link href="/stylesheets/style.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag "style.css" # =>
- # <link href="/stylesheets/style.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag "http://www.example.com/style.css" # =>
- # <link href="http://www.example.com/style.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag "style", :media => "all" # =>
- # <link href="/stylesheets/style.css" media="all" rel="stylesheet" />
- #
- # stylesheet_link_tag "style", :media => "print" # =>
- # <link href="/stylesheets/style.css" media="print" rel="stylesheet" />
- #
- # stylesheet_link_tag "random.styles", "/css/stylish" # =>
- # <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" />
- # <link href="/css/stylish.css" media="screen" rel="stylesheet" />
- #
- # You can also include all styles in the stylesheets directory using <tt>:all</tt> as the source:
- #
- # stylesheet_link_tag :all # =>
- # <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" />
- #
- # If you want Rails to search in all the subdirectories under stylesheets, you should explicitly set <tt>:recursive</tt>:
- #
- # stylesheet_link_tag :all, :recursive => true
- #
- # == Caching multiple stylesheets into one
- #
- # You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be
- # compressed by gzip (leading to faster transfers). Caching will only happen if +config.perform_caching+
- # is set to true (which is the case by default for the Rails production environment, but not for the development
- # environment). Examples:
- #
- # stylesheet_link_tag :all, :cache => true # when config.perform_caching is false =>
- # <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag :all, :cache => true # when config.perform_caching is true =>
- # <link href="/stylesheets/all.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when config.perform_caching is false =>
- # <link href="/stylesheets/shop.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/cart.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/checkout.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when config.perform_caching is true =>
- # <link href="/stylesheets/payment.css" media="screen" rel="stylesheet" />
- #
- # The <tt>:recursive</tt> option is also available for caching:
- #
- # stylesheet_link_tag :all, :cache => true, :recursive => true
- #
- # To force concatenation (even in development mode) set <tt>:concat</tt> to true. This is useful if
- # you have too many stylesheets for IE to load.
- #
- # stylesheet_link_tag :all, :concat => true
- #
- def stylesheet_link_tag(*sources)
- @stylesheet_include ||= StylesheetIncludeTag.new(config, asset_paths)
- @stylesheet_include.include_tag(*sources)
- end
-
- end
-
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/asset_url_helper.rb b/actionpack/lib/action_view/helpers/asset_url_helper.rb
new file mode 100644
index 0000000000..0bb5e739bb
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/asset_url_helper.rb
@@ -0,0 +1,355 @@
+require 'zlib'
+
+module ActionView
+ # = Action View Asset URL Helpers
+ module Helpers #:nodoc:
+ # This module provides methods for generating asset paths and
+ # urls.
+ #
+ # image_path("rails.png")
+ # # => "/assets/rails.png"
+ #
+ # image_url("rails.png")
+ # # => "http://www.example.com/assets/rails.png"
+ #
+ # === Using asset hosts
+ #
+ # By default, Rails links to these assets on the current host in the public
+ # folder, but you can direct Rails to link to assets from a dedicated asset
+ # server by setting <tt>ActionController::Base.asset_host</tt> in the application
+ # configuration, typically in <tt>config/environments/production.rb</tt>.
+ # For example, you'd define <tt>assets.example.com</tt> to be your asset
+ # host this way, inside the <tt>configure</tt> block of your environment-specific
+ # configuration files or <tt>config/application.rb</tt>:
+ #
+ # config.action_controller.asset_host = "assets.example.com"
+ #
+ # Helpers take that into account:
+ #
+ # image_tag("rails.png")
+ # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
+ # stylesheet_link_tag("application")
+ # # => <link href="http://assets.example.com/assets/application.css" media="screen" rel="stylesheet" />
+ #
+ # Browsers typically open at most two simultaneous connections to a single
+ # host, which means your assets often have to wait for other assets to finish
+ # downloading. You can alleviate this by using a <tt>%d</tt> wildcard in the
+ # +asset_host+. For example, "assets%d.example.com". If that wildcard is
+ # present Rails distributes asset requests among the corresponding four hosts
+ # "assets0.example.com", ..., "assets3.example.com". With this trick browsers
+ # will open eight simultaneous connections rather than two.
+ #
+ # image_tag("rails.png")
+ # # => <img alt="Rails" src="http://assets0.example.com/assets/rails.png" />
+ # stylesheet_link_tag("application")
+ # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
+ #
+ # To do this, you can either setup four actual hosts, or you can use wildcard
+ # DNS to CNAME the wildcard to a single asset host. You can read more about
+ # setting up your DNS CNAME records from your ISP.
+ #
+ # Note: This is purely a browser performance optimization and is not meant
+ # for server load balancing. See http://www.die.net/musings/page_load_time/
+ # for background.
+ #
+ # Alternatively, you can exert more control over the asset host by setting
+ # +asset_host+ to a proc like this:
+ #
+ # ActionController::Base.asset_host = Proc.new { |source|
+ # "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com"
+ # }
+ # image_tag("rails.png")
+ # # => <img alt="Rails" src="http://assets1.example.com/assets/rails.png" />
+ # stylesheet_link_tag("application")
+ # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
+ #
+ # The example above generates "http://assets1.example.com" and
+ # "http://assets2.example.com". This option is useful for example if
+ # you need fewer/more than four hosts, custom host names, etc.
+ #
+ # As you see the proc takes a +source+ parameter. That's a string with the
+ # absolute path of the asset, for example "/assets/rails.png".
+ #
+ # ActionController::Base.asset_host = Proc.new { |source|
+ # if source.ends_with?('.css')
+ # "http://stylesheets.example.com"
+ # else
+ # "http://assets.example.com"
+ # end
+ # }
+ # image_tag("rails.png")
+ # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
+ # stylesheet_link_tag("application")
+ # # => <link href="http://stylesheets.example.com/assets/application.css" media="screen" rel="stylesheet" />
+ #
+ # Alternatively you may ask for a second parameter +request+. That one is
+ # particularly useful for serving assets from an SSL-protected page. The
+ # example proc below disables asset hosting for HTTPS connections, while
+ # still sending assets for plain HTTP requests from asset hosts. If you don't
+ # have SSL certificates for each of the asset hosts this technique allows you
+ # to avoid warnings in the client about mixed media.
+ #
+ # config.action_controller.asset_host = Proc.new { |source, request|
+ # if request.ssl?
+ # "#{request.protocol}#{request.host_with_port}"
+ # else
+ # "#{request.protocol}assets.example.com"
+ # end
+ # }
+ #
+ # You can also implement a custom asset host object that responds to +call+
+ # and takes either one or two parameters just like the proc.
+ #
+ # config.action_controller.asset_host = AssetHostingWithMinimumSsl.new(
+ # "http://asset%d.example.com", "https://asset1.example.com"
+ # )
+ #
+ module AssetUrlHelper
+ URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
+
+ # Computes the path to asset in public directory. If :type
+ # options is set, a file extension will be appended and scoped
+ # to the corresponding public directory.
+ #
+ # All other asset *_path helpers delegate through this method.
+ #
+ # asset_path "application.js" # => /application.js
+ # asset_path "application", type: :javascript # => /javascripts/application.js
+ # asset_path "application", type: :stylesheet # => /stylesheets/application.css
+ # asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
+ def asset_path(source, options = {})
+ source = source.to_s
+ return "" unless source.present?
+ return source if source =~ URI_REGEXP
+
+ tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, '')
+
+ if extname = compute_asset_extname(source, options)
+ source = "#{source}#{extname}"
+ end
+
+ if source[0] != ?/
+ source = compute_asset_path(source, options)
+ end
+
+ relative_url_root = (defined?(config.relative_url_root) && config.relative_url_root) ||
+ (respond_to?(:request) && request.try(:script_name))
+ if relative_url_root
+ source = "#{relative_url_root}#{source}" unless source.starts_with?("#{relative_url_root}/")
+ end
+
+ if host = compute_asset_host(source, options)
+ source = "#{host}#{source}"
+ end
+
+ "#{source}#{tail}"
+ end
+ alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with a asset_path named route
+
+ # Computes the full URL to a asset in the public directory. This
+ # will use +asset_path+ internally, so most of their behaviors
+ # will be the same.
+ def asset_url(source, options = {})
+ path_to_asset(source, options.merge(:protocol => :request))
+ end
+ alias_method :url_to_asset, :asset_url # aliased to avoid conflicts with an asset_url named route
+
+ ASSET_EXTENSIONS = {
+ javascript: '.js',
+ stylesheet: '.css'
+ }
+
+ # Compute extname to append to asset path. Returns nil if
+ # nothing should be added.
+ def compute_asset_extname(source, options = {})
+ return if options[:extname] == false
+ extname = options[:extname] || ASSET_EXTENSIONS[options[:type]]
+ extname if extname && File.extname(source) != extname
+ end
+
+ # Maps asset types to public directory.
+ ASSET_PUBLIC_DIRECTORIES = {
+ audio: '/audios',
+ font: '/fonts',
+ image: '/images',
+ javascript: '/javascripts',
+ stylesheet: '/stylesheets',
+ video: '/videos'
+ }
+
+ # Computes asset path to public directory. Plugins and
+ # extensions can override this method to point to custom assets
+ # or generate digested paths or query strings.
+ def compute_asset_path(source, options = {})
+ dir = ASSET_PUBLIC_DIRECTORIES[options[:type]] || ""
+ File.join(dir, source)
+ end
+
+ # Pick an asset host for this source. Returns +nil+ if no host is set,
+ # the host if no wildcard is set, the host interpolated with the
+ # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
+ # or the value returned from invoking call on an object responding to call
+ # (proc or otherwise).
+ def compute_asset_host(source = "", options = {})
+ request = self.request if respond_to?(:request)
+ host = config.asset_host if defined? config.asset_host
+ host ||= request.base_url if request && options[:protocol] == :request
+ return unless host
+
+ if host.respond_to?(:call)
+ arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity
+ args = [source]
+ args << request if request && (arity > 1 || arity < 0)
+ host = host.call(*args)
+ elsif host =~ /%d/
+ host = host % (Zlib.crc32(source) % 4)
+ end
+
+ if host =~ URI_REGEXP
+ host
+ else
+ protocol = options[:protocol] || config.default_asset_host_protocol || (request ? :request : :relative)
+ case protocol
+ when :relative
+ "//#{host}"
+ when :request
+ "#{request.protocol}#{host}"
+ else
+ "#{protocol}://#{host}"
+ end
+ end
+ end
+
+ # Computes the path to a javascript asset in the public javascripts directory.
+ # If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
+ # Full paths from the document root will be passed through.
+ # Used internally by javascript_include_tag to build the script path.
+ #
+ # javascript_path "xmlhr" # => /javascripts/xmlhr.js
+ # javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
+ # javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
+ # javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr
+ # javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
+ def javascript_path(source, options = {})
+ path_to_asset(source, {type: :javascript}.merge!(options))
+ end
+ alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
+
+ # Computes the full URL to a javascript asset in the public javascripts directory.
+ # This will use +javascript_path+ internally, so most of their behaviors will be the same.
+ def javascript_url(source, options = {})
+ url_to_asset(source, {type: :javascript}.merge!(options))
+ end
+ alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
+
+ # Computes the path to a stylesheet asset in the public stylesheets directory.
+ # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
+ # Full paths from the document root will be passed through.
+ # Used internally by +stylesheet_link_tag+ to build the stylesheet path.
+ #
+ # stylesheet_path "style" # => /stylesheets/style.css
+ # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
+ # stylesheet_path "/dir/style.css" # => /dir/style.css
+ # stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style
+ # stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css
+ def stylesheet_path(source, options = {})
+ path_to_asset(source, {type: :stylesheet}.merge!(options))
+ end
+ alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
+
+ # Computes the full URL to a stylesheet asset in the public stylesheets directory.
+ # This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
+ def stylesheet_url(source, options = {})
+ url_to_asset(source, {type: :stylesheet}.merge!(options))
+ end
+ alias_method :url_to_stylesheet, :stylesheet_url # aliased to avoid conflicts with a stylesheet_url named route
+
+ # Computes the path to an image asset.
+ # Full paths from the document root will be passed through.
+ # Used internally by +image_tag+ to build the image path:
+ #
+ # image_path("edit") # => "/assets/edit"
+ # image_path("edit.png") # => "/assets/edit.png"
+ # image_path("icons/edit.png") # => "/assets/icons/edit.png"
+ # image_path("/icons/edit.png") # => "/icons/edit.png"
+ # image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
+ #
+ # If you have images as application resources this method may conflict with their named routes.
+ # The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
+ # plugin authors are encouraged to do so.
+ def image_path(source, options = {})
+ path_to_asset(source, {type: :image}.merge!(options))
+ end
+ alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
+
+ # Computes the full URL to an image asset.
+ # This will use +image_path+ internally, so most of their behaviors will be the same.
+ def image_url(source, options = {})
+ url_to_asset(source, {type: :image}.merge!(options))
+ end
+ alias_method :url_to_image, :image_url # aliased to avoid conflicts with an image_url named route
+
+ # Computes the path to a video asset in the public videos directory.
+ # Full paths from the document root will be passed through.
+ # Used internally by +video_tag+ to build the video path.
+ #
+ # video_path("hd") # => /videos/hd
+ # video_path("hd.avi") # => /videos/hd.avi
+ # video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
+ # video_path("/trailers/hd.avi") # => /trailers/hd.avi
+ # video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi
+ def video_path(source, options = {})
+ path_to_asset(source, {type: :video}.merge!(options))
+ end
+ alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
+
+ # Computes the full URL to a video asset in the public videos directory.
+ # This will use +video_path+ internally, so most of their behaviors will be the same.
+ def video_url(source, options = {})
+ url_to_asset(source, {type: :video}.merge!(options))
+ end
+ alias_method :url_to_video, :video_url # aliased to avoid conflicts with an video_url named route
+
+ # Computes the path to an audio asset in the public audios directory.
+ # Full paths from the document root will be passed through.
+ # Used internally by +audio_tag+ to build the audio path.
+ #
+ # audio_path("horse") # => /audios/horse
+ # audio_path("horse.wav") # => /audios/horse.wav
+ # audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav
+ # audio_path("/sounds/horse.wav") # => /sounds/horse.wav
+ # audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav
+ def audio_path(source, options = {})
+ path_to_asset(source, {type: :audio}.merge!(options))
+ end
+ alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
+
+ # Computes the full URL to an audio asset in the public audios directory.
+ # This will use +audio_path+ internally, so most of their behaviors will be the same.
+ def audio_url(source, options = {})
+ url_to_asset(source, {type: :audio}.merge!(options))
+ end
+ alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
+
+ # Computes the path to a font asset.
+ # Full paths from the document root will be passed through.
+ #
+ # font_path("font") # => /assets/font
+ # font_path("font.ttf") # => /assets/font.ttf
+ # font_path("dir/font.ttf") # => /assets/dir/font.ttf
+ # font_path("/dir/font.ttf") # => /dir/font.ttf
+ # font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
+ def font_path(source, options = {})
+ path_to_asset(source, {type: :font}.merge!(options))
+ end
+ alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
+
+ # Computes the full URL to a font asset.
+ # This will use +font_path+ internally, so most of their behaviors will be the same.
+ def font_url(source, options = {})
+ url_to_asset(source, {type: :font}.merge!(options))
+ end
+ alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb
index 59e1015976..ddac87a37d 100644
--- a/actionpack/lib/action_view/helpers/cache_helper.rb
+++ b/actionpack/lib/action_view/helpers/cache_helper.rb
@@ -2,11 +2,11 @@ module ActionView
# = Action View Cache Helper
module Helpers
module CacheHelper
- # This helper exposes a method for caching fragments of a view
+ # This helper exposes a method for caching fragments of a view
# rather than an entire action or page. This technique is useful
# caching pieces like menus, lists of newstopics, static HTML
# fragments, and so on. This method takes a block that contains
- # the content you wish to cache.
+ # the content you wish to cache.
#
# The best way to use this is by doing key-based cache expiration
# on top of a cache store like Memcached that'll automatically
@@ -56,40 +56,40 @@ module ActionView
#
# Most template dependencies can be derived from calls to render in the template itself.
# Here are some examples of render calls that Cache Digests knows how to decode:
- #
+ #
# render partial: "comments/comment", collection: commentable.comments
# render "comments/comments"
# render 'comments/comments'
# render('comments/comments')
- #
+ #
# render "header" => render("comments/header")
- #
+ #
# render(@topic) => render("topics/topic")
# 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:
- #
+ #
# render group_of_attachments
# render @project.documents.where(published: true).order('created_at')
- #
+ #
# You will have to rewrite those to the explicit form:
- #
+ #
# render partial: 'attachments/attachment', collection: group_of_attachments
# render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')
#
# === Explicit dependencies
- #
+ #
# Some times you'll have template dependencies that can't be derived at all. This is typically
# the case when you have template rendering that happens in helpers. Here's an example:
- #
+ #
# <%= render_sortable_todolists @project.todolists %>
- #
+ #
# You'll need to use a special comment format to call those out:
- #
+ #
# <%# 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.
# You can only declare one template dependency per line.
#
@@ -113,6 +113,17 @@ module ActionView
nil
end
+ def fragment_name_with_digest(name) #:nodoc:
+ if @virtual_path
+ [
+ *Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name),
+ Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context)
+ ]
+ else
+ name
+ end
+ end
+
private
# TODO: Create an object that has caching read/write on it
def fragment_for(name = {}, options = nil, &block) #:nodoc:
@@ -131,17 +142,6 @@ module ActionView
controller.write_fragment(name, fragment, options)
end
end
-
- def fragment_name_with_digest(name)
- if @virtual_path
- [
- *Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name),
- Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context)
- ]
- else
- name
- end
- end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 387dfeab17..f43d20c6ed 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -73,13 +73,17 @@ module ActionView
options[:include_seconds] ||= !!include_seconds_or_options
end
+ options = {
+ scope: :'datetime.distance_in_words'
+ }.merge!(options)
+
from_time = from_time.to_time if from_time.respond_to?(:to_time)
to_time = to_time.to_time if to_time.respond_to?(:to_time)
from_time, to_time = to_time, from_time if from_time > to_time
distance_in_minutes = ((to_time - from_time)/60.0).round
distance_in_seconds = (to_time - from_time).round
- I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale|
+ I18n.with_options :locale => options[:locale], :scope => options[:scope] do |locale|
case distance_in_minutes
when 0..1
return distance_in_minutes == 0 ?
@@ -196,6 +200,8 @@ module ActionView
# for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
# Setting this option prepends a select option with a generic prompt (Day, Month, Year, Hour, Minute, Seconds)
# or the given prompt string.
+ # * <tt>:with_css_classes</tt> - Set to true if you want assign different styles for 'select' tags. This option
+ # automatically set classes 'year', 'month', 'day', 'hour', 'minute' and 'second' for your 'select' tags.
#
# If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
#
@@ -937,6 +943,7 @@ module ActionView
:name => input_name_from_type(type)
}.merge!(@html_options)
select_options[:disabled] = 'disabled' if @options[:disabled]
+ select_options[:class] = type if @options[:with_css_classes]
select_html = "\n"
select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 0bb08cd7ff..b87c2e936f 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -207,7 +207,7 @@ module ActionView
# if the object's class is +Post+. However, this can be overwritten using
# the <tt>:as</tt> option, e.g. -
#
- # <%= form_for(@person, :as => :client) do |f| %>
+ # <%= form_for(@person, as: :client) do |f| %>
# ...
# <% end %>
#
@@ -242,7 +242,7 @@ module ActionView
#
# is then equivalent to something like:
#
- # <%= form_for @post, :as => :post, :url => post_path(@post), :method => :put, :html => { :class => "edit_post", :id => "edit_post_45" } do |f| %>
+ # <%= form_for @post, as: :post, url: post_path(@post), method: :put, html: { class: "edit_post", id: "edit_post_45" } do |f| %>
# ...
# <% end %>
#
@@ -254,19 +254,19 @@ module ActionView
#
# is equivalent to something like:
#
- # <%= form_for @post, :as => :post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
+ # <%= form_for @post, as: :post, url: posts_path, html: { class: "new_post", id: "new_post" } do |f| %>
# ...
# <% end %>
#
# However you can still overwrite individual conventions, such as:
#
- # <%= form_for(@post, :url => super_posts_path) do |f| %>
+ # <%= form_for(@post, url: super_posts_path) do |f| %>
# ...
# <% end %>
#
# You can also set the answer format, like this:
#
- # <%= form_for(@post, :format => :json) do |f| %>
+ # <%= form_for(@post, format: :json) do |f| %>
# ...
# <% end %>
#
@@ -290,7 +290,7 @@ module ActionView
#
# You can force the form to use the full array of HTTP verbs by setting
#
- # :method => (:get|:post|:patch|:put|:delete)
+ # method: (:get|:post|:patch|:put|:delete)
#
# in the options hash. If the verb is not GET or POST, which are natively
# supported by HTML forms, the form will be set to POST and a hidden input
@@ -300,7 +300,7 @@ module ActionView
#
# Specifying:
#
- # :remote => true
+ # remote: true
#
# in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its
# behavior. The expected default behavior is an XMLHttpRequest in the background instead of the regular
@@ -310,7 +310,7 @@ module ActionView
#
# Example:
#
- # <%= form_for(@post, :remote => true) do |f| %>
+ # <%= form_for(@post, remote: true) do |f| %>
# ...
# <% end %>
#
@@ -354,7 +354,7 @@ module ActionView
# Example:
#
# <%= form_for(@post) do |f| %>
- # <%= f.fields_for(:comments, :include_id => false) do |cf| %>
+ # <%= f.fields_for(:comments, include_id: false) do |cf| %>
# ...
# <% end %>
# <% end %>
@@ -366,7 +366,7 @@ module ActionView
# custom builder. For example, let's say you made a helper to
# automatically add labels to form inputs.
#
- # <%= form_for @person, :url => { :action => "create" }, :builder => LabellingFormBuilder do |f| %>
+ # <%= form_for @person, url: { action: "create" }, builder: LabellingFormBuilder do |f| %>
# <%= f.text_field :first_name %>
# <%= f.text_field :last_name %>
# <%= f.text_area :biography %>
@@ -390,7 +390,7 @@ module ActionView
#
# def labelled_form_for(record_or_name_or_array, *args, &proc)
# options = args.extract_options!
- # form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &proc)
+ # form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &proc)
# end
#
# If you don't need to attach a form to a model instance, then check out
@@ -403,13 +403,13 @@ module ActionView
#
# To set an authenticity token you need to pass an <tt>:authenticity_token</tt> parameter
#
- # <%= form_for @invoice, :url => external_url, :authenticity_token => 'external_token' do |f|
+ # <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f|
# ...
# <% end %>
#
# If you don't want to an authenticity token field be rendered at all just pass <tt>false</tt>:
#
- # <%= form_for @invoice, :url => external_url, :authenticity_token => false do |f|
+ # <%= form_for @invoice, url: external_url, authenticity_token: false do |f|
# ...
# <% end %>
def form_for(record, options = {}, &proc)
@@ -423,7 +423,7 @@ module ActionView
object = nil
else
object = record.is_a?(Array) ? record.last : record
- raise ArgumentError, "First argument in form cannot contain nil or be empty" if object.blank?
+ raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
object_name = options[:as] || model_name_from_record_or_class(object).param_key
apply_form_for_options!(record, object, options)
end
@@ -435,7 +435,7 @@ module ActionView
builder = options[:parent_builder] = instantiate_builder(object_name, object, options)
fields_for = fields_for(object_name, object, options, &proc)
- default_options = builder.multipart? ? { :multipart => true } : {}
+ default_options = builder.multipart? ? { multipart: true } : {}
default_options.merge!(options.delete(:html))
form_tag(options.delete(:url) || {}, default_options) { fields_for }
@@ -447,12 +447,12 @@ module ActionView
as = options[:as]
action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :patch] : [:new, :post]
options[:html].reverse_merge!(
- :class => as ? "#{action}_#{as}" : dom_class(object, action),
- :id => as ? "#{action}_#{as}" : [options[:namespace], dom_id(object, action)].compact.join("_").presence,
- :method => method
+ class: as ? "#{action}_#{as}" : dom_class(object, action),
+ id: as ? "#{action}_#{as}" : [options[:namespace], dom_id(object, action)].compact.join("_").presence,
+ method: method
)
- options[:url] ||= polymorphic_path(record, :format => options.delete(:format))
+ options[:url] ||= polymorphic_path(record, format: options.delete(:format))
end
private :apply_form_for_options!
@@ -578,7 +578,7 @@ module ActionView
#
# class Person < ActiveRecord::Base
# has_one :address
- # accepts_nested_attributes_for :address, :allow_destroy => true
+ # accepts_nested_attributes_for :address, allow_destroy: true
# end
#
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
@@ -674,7 +674,7 @@ module ActionView
#
# class Person < ActiveRecord::Base
# has_many :projects
- # accepts_nested_attributes_for :projects, :allow_destroy => true
+ # accepts_nested_attributes_for :projects, allow_destroy: true
# end
#
# This will allow you to specify which models to destroy in the
@@ -747,10 +747,10 @@ module ActionView
# label(:post, :title, "A short title")
# # => <label for="post_title">A short title</label>
#
- # label(:post, :title, "A short title", :class => "title_label")
+ # label(:post, :title, "A short title", class: "title_label")
# # => <label for="post_title" class="title_label">A short title</label>
#
- # label(:post, :privacy, "Public Post", :value => "public")
+ # label(:post, :privacy, "Public Post", value: "public")
# # => <label for="post_privacy_public">Public Post</label>
#
# label(:post, :terms) do
@@ -766,18 +766,17 @@ module ActionView
# shown.
#
# ==== Examples
- # text_field(:post, :title, :size => 20)
+ # text_field(:post, :title, size: 20)
# # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
#
- # text_field(:post, :title, :class => "create_input")
+ # text_field(:post, :title, class: "create_input")
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
#
- # text_field(:session, :user, :onchange => "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }")
+ # text_field(:session, :user, onchange: "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }")
# # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange = "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }"/>
#
- # text_field(:snippet, :code, :size => 20, :class => 'code_input')
+ # text_field(:snippet, :code, size: 20, class: 'code_input')
# # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
- #
def text_field(object_name, method, options = {})
Tags::TextField.new(object_name, method, self, options).render
end
@@ -788,18 +787,17 @@ module ActionView
# shown. For security reasons this field is blank by default; pass in a value via +options+ if this is not desired.
#
# ==== Examples
- # password_field(:login, :pass, :size => 20)
+ # password_field(:login, :pass, size: 20)
# # => <input type="password" id="login_pass" name="login[pass]" size="20" />
#
- # password_field(:account, :secret, :class => "form_input", :value => @account.secret)
+ # password_field(:account, :secret, class: "form_input", value: @account.secret)
# # => <input type="password" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
#
- # password_field(:user, :password, :onchange => "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }")
+ # password_field(:user, :password, onchange: "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }")
# # => <input type="password" id="user_password" name="user[password]" onchange = "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }"/>
#
- # password_field(:account, :pin, :size => 20, :class => 'form_input')
+ # password_field(:account, :pin, size: 20, class: 'form_input')
# # => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" />
- #
def password_field(object_name, method, options = {})
Tags::PasswordField.new(object_name, method, self, options).render
end
@@ -833,12 +831,11 @@ module ActionView
# file_field(:user, :avatar)
# # => <input type="file" id="user_avatar" name="user[avatar]" />
#
- # file_field(:post, :attached, :accept => 'text/html')
+ # file_field(:post, :attached, accept: 'text/html')
# # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
#
- # file_field(:attachment, :file, :class => 'file_input')
+ # file_field(:attachment, :file, class: 'file_input')
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
- #
def file_field(object_name, method, options = {})
Tags::FileField.new(object_name, method, self, options).render
end
@@ -848,22 +845,22 @@ module ActionView
# hash with +options+.
#
# ==== Examples
- # text_area(:post, :body, :cols => 20, :rows => 40)
+ # text_area(:post, :body, cols: 20, rows: 40)
# # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
# # #{@post.body}
# # </textarea>
#
- # text_area(:comment, :text, :size => "20x30")
+ # text_area(:comment, :text, size: "20x30")
# # => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
# # #{@comment.text}
# # </textarea>
#
- # text_area(:application, :notes, :cols => 40, :rows => 15, :class => 'app_input')
+ # text_area(:application, :notes, cols: 40, rows: 15, class: 'app_input')
# # => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
# # #{@application.notes}
# # </textarea>
#
- # text_area(:entry, :body, :size => "20x20", :disabled => 'disabled')
+ # text_area(:entry, :body, size: "20x20", disabled: 'disabled')
# # => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
# # #{@entry.body}
# # </textarea>
@@ -902,7 +899,7 @@ module ActionView
# Unfortunately that workaround does not work when the check box goes
# within an array-like parameter, as in
#
- # <%= fields_for "project[invoice_attributes][]", invoice, :index => nil do |form| %>
+ # <%= fields_for "project[invoice_attributes][]", invoice, index: nil do |form| %>
# <%= form.check_box :paid %>
# ...
# <% end %>
@@ -924,10 +921,9 @@ module ActionView
# # => <input name="puppy[gooddog]" type="hidden" value="no" />
# # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
#
- # check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no")
+ # check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
# # => <input name="eula[accepted]" type="hidden" value="no" />
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
- #
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
Tags::CheckBox.new(object_name, method, self, checked_value, unchecked_value, options).render
end
@@ -936,7 +932,7 @@ module ActionView
# assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
# radio button will be checked.
#
- # To force the radio button to be checked pass <tt>:checked => true</tt> in the
+ # To force the radio button to be checked pass <tt>checked: true</tt> in the
# +options+ hash. You may pass HTML options there as well.
#
# # Let's say that @post.category returns "rails":
@@ -957,7 +953,6 @@ module ActionView
#
# color_field("car", "color")
# # => <input id="car_color" name="car[color]" type="color" value="#000000" />
- #
def color_field(object_name, method, options = {})
Tags::ColorField.new(object_name, method, self, options).render
end
@@ -968,18 +963,18 @@ module ActionView
#
# search_field(:user, :name)
# # => <input id="user_name" name="user[name]" type="search" />
- # search_field(:user, :name, :autosave => false)
+ # search_field(:user, :name, autosave: false)
# # => <input autosave="false" id="user_name" name="user[name]" type="search" />
- # search_field(:user, :name, :results => 3)
+ # search_field(:user, :name, results: 3)
# # => <input id="user_name" name="user[name]" results="3" type="search" />
# # Assume request.host returns "www.example.com"
- # search_field(:user, :name, :autosave => true)
+ # search_field(:user, :name, autosave: true)
# # => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" type="search" />
- # search_field(:user, :name, :onsearch => true)
+ # search_field(:user, :name, onsearch: true)
# # => <input id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
- # search_field(:user, :name, :autosave => false, :onsearch => true)
+ # search_field(:user, :name, autosave: false, onsearch: true)
# # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
- # search_field(:user, :name, :autosave => true, :onsearch => true)
+ # search_field(:user, :name, autosave: true, onsearch: true)
# # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" type="search" />
def search_field(object_name, method, options = {})
Tags::SearchField.new(object_name, method, self, options).render
@@ -1351,7 +1346,7 @@ module ActionView
private
def objectify_options(options)
- @default_options.merge(options.merge(:object => @object))
+ @default_options.merge(options.merge(object: @object))
end
def submit_default_value
@@ -1369,7 +1364,7 @@ module ActionView
defaults << :"helpers.submit.#{key}"
defaults << "#{key.to_s.humanize} #{model}"
- I18n.t(defaults.shift, :model => model, :default => defaults)
+ I18n.t(defaults.shift, model: model, default: defaults)
end
def nested_attributes_association?(association_name)
diff --git a/actionpack/lib/action_view/helpers/tags/check_box.rb b/actionpack/lib/action_view/helpers/tags/check_box.rb
index 9d17a1dde3..e21cc07746 100644
--- a/actionpack/lib/action_view/helpers/tags/check_box.rb
+++ b/actionpack/lib/action_view/helpers/tags/check_box.rb
@@ -46,10 +46,12 @@ module ActionView
false
when String
value == @checked_value
- when Array
- value.include?(@checked_value)
else
- value.to_i == @checked_value.to_i
+ if value.respond_to?(:include?)
+ value.include?(@checked_value)
+ else
+ value.to_i == @checked_value.to_i
+ end
end
end
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 3f65791aa0..5105d0e585 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -46,7 +46,6 @@ module ActionView
end
protected :_back_url
-
# Creates a link tag of the given +name+ using a URL created by the set of +options+.
# See the valid options in the documentation for +url_for+. It's also possible to
# pass a String instead of an options hash, which generates a link tag that uses the
@@ -115,7 +114,7 @@ module ActionView
#
# in place of the older more verbose, non-resource-oriented
#
- # link_to "Profile", :controller => "profiles", :action => "show", :id => @profile
+ # link_to "Profile", controller: "profiles", action: "show", id: @profile
# # => <a href="/profiles/show/1">Profile</a>
#
# Similarly,
@@ -125,7 +124,7 @@ module ActionView
#
# is better than
#
- # link_to "Profiles", :controller => "profiles"
+ # link_to "Profiles", controller: "profiles"
# # => <a href="/profiles">Profiles</a>
#
# You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
@@ -139,45 +138,46 @@ module ActionView
#
# Classes and ids for CSS are easy to produce:
#
- # link_to "Articles", articles_path, :id => "news", :class => "article"
+ # link_to "Articles", articles_path, id: "news", class: "article"
# # => <a href="/articles" class="article" id="news">Articles</a>
#
# Be careful when using the older argument style, as an extra literal hash is needed:
#
- # link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
+ # link_to "Articles", { controller: "articles" }, id: "news", class: "article"
# # => <a href="/articles" class="article" id="news">Articles</a>
#
# Leaving the hash off gives the wrong link:
#
- # link_to "WRONG!", :controller => "articles", :id => "news", :class => "article"
+ # link_to "WRONG!", controller: "articles", id: "news", class: "article"
# # => <a href="/articles/index/news?class=article">WRONG!</a>
#
# +link_to+ can also produce links with anchors or query strings:
#
- # link_to "Comment wall", profile_path(@profile, :anchor => "wall")
+ # link_to "Comment wall", profile_path(@profile, anchor: "wall")
# # => <a href="/profiles/1#wall">Comment wall</a>
#
- # link_to "Ruby on Rails search", :controller => "searches", :query => "ruby on rails"
+ # link_to "Ruby on Rails search", controller: "searches", query: "ruby on rails"
# # => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
#
- # link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
+ # link_to "Nonsense search", searches_path(foo: "bar", baz: "quux")
# # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
#
# The only option specific to +link_to+ (<tt>:method</tt>) is used as follows:
#
- # link_to("Destroy", "http://www.example.com", :method => :delete)
+ # link_to("Destroy", "http://www.example.com", method: :delete)
# # => <a href='http://www.example.com' rel="nofollow" data-method="delete">Destroy</a>
#
# You can also use custom data attributes using the <tt>:data</tt> option:
#
- # link_to "Visit Other Site", "http://www.rubyonrails.org/", :data => { :confirm => "Are you sure?" }
+ # link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
# # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?"">Visit Other Site</a>
def link_to(name = nil, options = nil, html_options = nil, &block)
html_options, options = options, name if block_given?
options ||= {}
- url = url_for(options)
html_options = convert_options_to_data_attributes(options, html_options)
+
+ url = url_for(options)
html_options['href'] ||= url
content_tag(:a, name || url, html_options, &block)
@@ -196,7 +196,7 @@ module ActionView
# the form submission and input element behavior using +html_options+.
# This method accepts the <tt>:method</tt> modifier described in the +link_to+ documentation.
# If no <tt>:method</tt> modifier is given, it will default to performing a POST operation.
- # You can also disable the button by passing <tt>:disabled => true</tt> in +html_options+.
+ # You can also disable the button by passing <tt>disabled: true</tt> in +html_options+.
# If you are using RESTful routes, you can pass the <tt>:method</tt>
# to change the HTTP verb used to submit the form.
#
@@ -225,7 +225,7 @@ module ActionView
# by the unobtrusive JavaScript driver.
#
# ==== Examples
- # <%= button_to "New", :action => "new" %>
+ # <%= button_to "New", action: "new" %>
# # => "<form method="post" action="/controller/new" class="button_to">
# # <div><input value="New" type="submit" /></div>
# # </form>"
@@ -241,13 +241,13 @@ module ActionView
# # </div>
# # </form>"
#
- # <%= button_to "New", :action => "new", :form_class => "new-thing" %>
+ # <%= button_to "New", action: "new", form_class: "new-thing" %>
# # => "<form method="post" action="/controller/new" class="new-thing">
# # <div><input value="New" type="submit" /></div>
# # </form>"
#
#
- # <%= button_to "Create", :action => "create", :remote => true, :form => { "data-type" => "json" } %>
+ # <%= button_to "Create", action: "create", remote: true, form: { "data-type" => "json" } %>
# # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
# # <div>
# # <input value="Create" type="submit" />
@@ -256,8 +256,8 @@ module ActionView
# # </form>"
#
#
- # <%= button_to "Delete Image", { :action => "delete", :id => @image.id },
- # :method => :delete, :data => { :confirm => "Are you sure?" } %>
+ # <%= button_to "Delete Image", { action: "delete", id: @image.id },
+ # method: :delete, data: { confirm: "Are you sure?" } %>
# # => "<form method="post" action="/images/delete/1" class="button_to">
# # <div>
# # <input type="hidden" name="_method" value="delete" />
@@ -268,7 +268,7 @@ module ActionView
#
#
# <%= button_to('Destroy', 'http://www.example.com',
- # :method => "delete", :remote => true, :data => { :confirm' => 'Are you sure?', :disable_with => 'loading...' }) %>
+ # method: "delete", remote: true, data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
# # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
# # <div>
# # <input name='_method' value='delete' type='hidden' />
@@ -294,7 +294,7 @@ module ActionView
form_method = method == 'get' ? 'get' : 'post'
form_options = html_options.delete('form') || {}
form_options[:class] ||= html_options.delete('form_class') || 'button_to'
- form_options.merge!(:method => form_method, :action => url)
+ form_options.merge!(method: form_method, action: url)
form_options.merge!("data-remote" => "true") if remote
request_token_tag = form_method == 'post' ? token_tag : ''
@@ -313,7 +313,6 @@ module ActionView
content_tag('form', content_tag('div', inner_tags), form_options)
end
-
# Creates a link tag of the given +name+ using a URL created by the set of
# +options+ unless the current request URI is the same as the links, in
# which case only the name is returned (or the given block is yielded, if
@@ -325,8 +324,8 @@ module ActionView
# Let's say you have a navigation menu...
#
# <ul id="navbar">
- # <li><%= link_to_unless_current("Home", { :action => "index" }) %></li>
- # <li><%= link_to_unless_current("About Us", { :action => "about" }) %></li>
+ # <li><%= link_to_unless_current("Home", { action: "index" }) %></li>
+ # <li><%= link_to_unless_current("About Us", { action: "about" }) %></li>
# </ul>
#
# If in the "about" action, it will render...
@@ -348,8 +347,8 @@ module ActionView
# "Go Back" link instead of a link to the comments page, we could do something like this...
#
# <%=
- # link_to_unless_current("Comment", { :controller => "comments", :action => "new" }) do
- # link_to("Go back", { :controller => "posts", :action => "index" })
+ # link_to_unless_current("Comment", { controller: "comments", action: "new" }) do
+ # link_to("Go back", { controller: "posts", action: "index" })
# end
# %>
def link_to_unless_current(name, options = {}, html_options = {}, &block)
@@ -363,13 +362,13 @@ module ActionView
# accepts the name or the full argument list for +link_to_unless+.
#
# ==== Examples
- # <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) %>
+ # <%= link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) %>
# # If the user is logged in...
# # => <a href="/controller/reply/">Reply</a>
#
# <%=
- # link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) do |name|
- # link_to(name, { :controller => "accounts", :action => "signup" })
+ # link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) do |name|
+ # link_to(name, { controller: "accounts", action: "signup" })
# end
# %>
# # If the user is logged in...
@@ -395,13 +394,13 @@ module ActionView
# in +link_to_unless+).
#
# ==== Examples
- # <%= link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) %>
+ # <%= link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) %>
# # If the user isn't logged in...
# # => <a href="/sessions/new/">Login</a>
#
# <%=
- # link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) do
- # link_to(@current_user.login, { :controller => "accounts", :action => "show", :id => @current_user })
+ # link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) do
+ # link_to(@current_user.login, { controller: "accounts", action: "show", id: @current_user })
# end
# %>
# # If the user isn't logged in...
@@ -442,17 +441,17 @@ module ActionView
# mail_to "me@domain.com"
# # => <a href="mailto:me@domain.com">me@domain.com</a>
#
- # mail_to "me@domain.com", "My email", :encode => "javascript"
+ # mail_to "me@domain.com", "My email", encode: "javascript"
# # => <script>eval(decodeURIComponent('%64%6f%63...%27%29%3b'))</script>
#
- # mail_to "me@domain.com", "My email", :encode => "hex"
+ # mail_to "me@domain.com", "My email", encode: "hex"
# # => <a href="mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>
#
- # mail_to "me@domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email"
+ # mail_to "me@domain.com", nil, replace_at: "_at_", replace_dot: "_dot_", class: "email"
# # => <a href="mailto:me@domain.com" class="email">me_at_domain_dot_com</a>
#
- # mail_to "me@domain.com", "My email", :cc => "ccaddress@domain.com",
- # :subject => "This is an example email"
+ # mail_to "me@domain.com", "My email", cc: "ccaddress@domain.com",
+ # subject: "This is an example email"
# # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
def mail_to(email_address, name = nil, html_options = {})
email_address = ERB::Util.html_escape(email_address)
@@ -501,47 +500,47 @@ module ActionView
# ==== Examples
# Let's say we're in the <tt>/shop/checkout?order=desc</tt> action.
#
- # current_page?(:action => 'process')
+ # current_page?(action: 'process')
# # => false
#
- # current_page?(:controller => 'shop', :action => 'checkout')
+ # current_page?(controller: 'shop', action: 'checkout')
# # => true
#
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'asc')
+ # current_page?(controller: 'shop', action: 'checkout', order: 'asc')
# # => false
#
- # current_page?(:action => 'checkout')
+ # current_page?(action: 'checkout')
# # => true
#
- # current_page?(:controller => 'library', :action => 'checkout')
+ # current_page?(controller: 'library', action: 'checkout')
# # => false
#
# Let's say we're in the <tt>/shop/checkout?order=desc&page=1</tt> action.
#
- # current_page?(:action => 'process')
+ # current_page?(action: 'process')
# # => false
#
- # current_page?(:controller => 'shop', :action => 'checkout')
+ # current_page?(controller: 'shop', action: 'checkout')
# # => true
#
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '1')
+ # current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '1')
# # => true
#
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '2')
+ # current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '2')
# # => false
#
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc')
+ # current_page?(controller: 'shop', action: 'checkout', order: 'desc')
# # => false
#
- # current_page?(:action => 'checkout')
+ # current_page?(action: 'checkout')
# # => true
#
- # current_page?(:controller => 'library', :action => 'checkout')
+ # current_page?(controller: 'library', action: 'checkout')
# # => false
#
# Let's say we're in the <tt>/products</tt> action with method POST in case of invalid product.
#
- # current_page?(:controller => 'product', :action => 'index')
+ # current_page?(controller: 'product', action: 'index')
# # => false
#
def current_page?(options)
@@ -598,7 +597,9 @@ module ActionView
end
def link_to_remote_options?(options)
- options.is_a?(Hash) && options.delete('remote')
+ if options.is_a?(Hash)
+ options.delete('remote') || options.delete(:remote)
+ end
end
def add_method_to_attributes!(html_options, method)
@@ -639,14 +640,14 @@ module ActionView
def token_tag(token=nil)
if token != false && protect_against_forgery?
token ||= form_authenticity_token
- tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => token)
+ tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
else
''
end
end
def method_tag(method)
- tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
+ tag('input', type: 'hidden', name: '_method', value: method.to_s)
end
end
end
diff --git a/actionpack/lib/action_view/log_subscriber.rb b/actionpack/lib/action_view/log_subscriber.rb
index cc3a871576..fd9a543e0a 100644
--- a/actionpack/lib/action_view/log_subscriber.rb
+++ b/actionpack/lib/action_view/log_subscriber.rb
@@ -3,10 +3,13 @@ module ActionView
#
# Provides functionality so that Rails can output logs from Action View.
class LogSubscriber < ActiveSupport::LogSubscriber
+ VIEWS_PATTERN = /^app\/views\//.freeze
+
def render_template(event)
+ return unless logger.info?
message = " Rendered #{from_rails_root(event.payload[:identifier])}"
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
- message << (" (%.1fms)" % event.duration)
+ message << " (#{event.duration.round(1)}ms)"
info(message)
end
alias :render_partial :render_template
@@ -19,7 +22,7 @@ module ActionView
protected
def from_rails_root(string)
- string.sub("#{Rails.root}/", "").sub(/^app\/views\//, "")
+ string.sub("#{Rails.root}/", "").sub(VIEWS_PATTERN, "")
end
end
end
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index 2d36deaa78..3875d88a9f 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -5,8 +5,6 @@ module ActionView
# = Action View Railtie
class Railtie < Rails::Railtie
config.action_view = ActiveSupport::OrderedOptions.new
- config.action_view.stylesheet_expansions = {}
- config.action_view.javascript_expansions = { :defaults => %w(jquery jquery_ujs) }
config.action_view.embed_authenticity_token_in_remote_forms = false
config.eager_load_namespaces << ActionView
@@ -22,26 +20,6 @@ module ActionView
ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
end
- initializer "action_view.cache_asset_ids" do |app|
- unless app.config.cache_classes
- ActiveSupport.on_load(:action_view) do
- ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
- end
- end
- end
-
- initializer "action_view.javascript_expansions" do |app|
- ActiveSupport.on_load(:action_view) do
- ActionView::Helpers::AssetTagHelper.register_javascript_expansion(
- app.config.action_view.delete(:javascript_expansions)
- )
-
- ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion(
- app.config.action_view.delete(:stylesheet_expansions)
- )
- end
- end
-
initializer "action_view.set_configs" do |app|
ActiveSupport.on_load(:action_view) do
app.config.action_view.each do |k,v|
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index b7fd6ec7e1..a548b44780 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -119,8 +119,29 @@ module ActionView
output
end
- def locals
- @locals ||= {}
+ def rendered_views
+ @_rendered_views ||= RenderedViewsCollection.new
+ end
+
+ class RenderedViewsCollection
+ def initialize
+ @rendered_views ||= {}
+ end
+
+ def add(view, locals)
+ @rendered_views[view] ||= []
+ @rendered_views[view] << locals
+ end
+
+ def locals_for(view)
+ @rendered_views[view]
+ end
+
+ def view_rendered?(view, expected_locals)
+ locals_for(view).any? do |actual_locals|
+ expected_locals.all? {|key, value| value == actual_locals[key] }
+ end
+ end
end
included do
@@ -156,18 +177,18 @@ module ActionView
end
module Locals
- attr_accessor :locals
+ attr_accessor :rendered_views
def render(options = {}, local_assigns = {})
case options
when Hash
if block_given?
- locals[options[:layout]] = options[:locals]
+ rendered_views.add options[:layout], options[:locals]
elsif options.key?(:partial)
- locals[options[:partial]] = options[:locals]
+ rendered_views.add options[:partial], options[:locals]
end
else
- locals[options] = local_assigns
+ rendered_views.add options, local_assigns
end
super
@@ -180,7 +201,7 @@ module ActionView
view = @controller.view_context
view.singleton_class.send :include, _helpers
view.extend(Locals)
- view.locals = self.locals
+ view.rendered_views = self.rendered_views
view.output_buffer = self.output_buffer
view
end
@@ -196,17 +217,17 @@ module ActionView
:@_result,
:@_routes,
:@controller,
- :@layouts,
- :@locals,
+ :@_layouts,
+ :@_rendered_views,
:@method_name,
:@output_buffer,
- :@partials,
+ :@_partials,
:@passed,
:@rendered,
:@request,
:@routes,
:@tagged_logger,
- :@templates,
+ :@_templates,
:@options,
:@test_passed,
:@view,
diff --git a/actionpack/lib/action_view/vendor/html-scanner/html/selector.rb b/actionpack/lib/action_view/vendor/html-scanner/html/selector.rb
index 1eadfc0390..60b6783b19 100644
--- a/actionpack/lib/action_view/vendor/html-scanner/html/selector.rb
+++ b/actionpack/lib/action_view/vendor/html-scanner/html/selector.rb
@@ -160,7 +160,7 @@ module HTML
# * <tt>:not(selector)</tt> -- Match the element only if the element does not
# match the simple selector.
#
- # As you can see, <tt>:nth-child<tt> pseudo class and its variant can get quite
+ # As you can see, <tt>:nth-child</tt> pseudo class and its variant can get quite
# tricky and the CSS specification doesn't do a much better job explaining it.
# But after reading the examples and trying a few combinations, it's easy to
# figure out.
diff --git a/actionpack/test/active_record_unit.rb b/actionpack/test/active_record_unit.rb
index 4f2b052720..4dd7406798 100644
--- a/actionpack/test/active_record_unit.rb
+++ b/actionpack/test/active_record_unit.rb
@@ -11,7 +11,7 @@ class ActiveRecordTestConnector
end
# Try to grab AR
-unless defined?(ActiveRecord) && defined?(Fixtures)
+unless defined?(ActiveRecord) && defined?(FixtureSet)
begin
PATH_TO_AR = "#{File.dirname(__FILE__)}/../../activerecord/lib"
raise LoadError, "#{PATH_TO_AR} doesn't exist" unless File.directory?(PATH_TO_AR)
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 620479cb0c..65c18dfb64 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -6,744 +6,40 @@ CACHE_DIR = 'test_cache'
# Don't change '/../temp/' cavalierly or you might hose something you don't want hosed
FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR)
-class CachingMetalController < ActionController::Metal
+class FragmentCachingMetalTestController < ActionController::Metal
abstract!
include ActionController::Caching
- self.page_cache_directory = FILE_STORE_PATH
- self.cache_store = :file_store, FILE_STORE_PATH
-end
-
-class PageCachingMetalTestController < CachingMetalController
- caches_page :ok
-
- def ok
- self.response_body = 'ok'
- end
+ def some_action; end
end
-class PageCachingMetalTest < ActionController::TestCase
- tests PageCachingMetalTestController
-
+class FragmentCachingMetalTest < ActionController::TestCase
def setup
- FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
- FileUtils.mkdir_p(FILE_STORE_PATH)
- end
-
- def teardown
- FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
+ super
+ @store = ActiveSupport::Cache::MemoryStore.new
+ @controller = FragmentCachingMetalTestController.new
+ @controller.perform_caching = true
+ @controller.cache_store = @store
+ @params = { controller: 'posts', action: 'index'}
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller.params = @params
+ @controller.request = @request
+ @controller.response = @response
end
- def test_should_cache_get_with_ok_status
- get :ok
- assert_response :ok
- assert File.exist?("#{FILE_STORE_PATH}/page_caching_metal_test/ok.html"), 'get with ok status should have been cached'
+ def test_fragment_cache_key
+ assert_equal 'views/what a key', @controller.fragment_cache_key('what a key')
end
end
-ActionController::Base.page_cache_directory = FILE_STORE_PATH
-
class CachingController < ActionController::Base
abstract!
self.cache_store = :file_store, FILE_STORE_PATH
end
-class PageCachingTestController < CachingController
- self.page_cache_compression = :best_compression
-
- caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? }
- caches_page :found, :not_found
- caches_page :about_me
- caches_page :default_gzip
- caches_page :no_gzip, :gzip => false
- caches_page :gzip_level, :gzip => :best_speed
-
- def ok
- head :ok
- end
-
- def no_content
- head :no_content
- end
-
- def found
- redirect_to :action => 'ok'
- end
-
- def not_found
- head :not_found
- end
-
- def custom_path
- render :text => "Super soaker"
- cache_page("Super soaker", "/index.html")
- end
-
- def default_gzip
- render :text => "Text"
- end
-
- def no_gzip
- render :text => "PNG"
- end
-
- def gzip_level
- render :text => "Big text"
- end
-
- def expire_custom_path
- expire_page("/index.html")
- head :ok
- end
-
- def trailing_slash
- render :text => "Sneak attack"
- end
-
- def about_me
- respond_to do |format|
- format.html {render :text => 'I am html'}
- format.xml {render :text => 'I am xml'}
- end
- end
-
-end
-
-class PageCachingTest < ActionController::TestCase
- def setup
- super
-
- @request = ActionController::TestRequest.new
- @request.host = 'hostname.com'
- @request.env.delete('PATH_INFO')
-
- @controller = PageCachingTestController.new
- @controller.perform_caching = true
- @controller.cache_store = :file_store, FILE_STORE_PATH
-
- @response = ActionController::TestResponse.new
-
- @params = {:controller => 'posts', :action => 'index', :only_path => true}
-
- FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
- FileUtils.mkdir_p(FILE_STORE_PATH)
- end
-
- def teardown
- FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
- @controller.perform_caching = false
- end
-
- def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route
- with_routing do |set|
- set.draw do
- get 'posts.:format', :to => 'posts#index', :as => :formatted_posts
- get '/', :to => 'posts#index', :as => :main
- end
- @params[:format] = 'rss'
- assert_equal '/posts.rss', @routes.url_for(@params)
- @params[:format] = nil
- assert_equal '/', @routes.url_for(@params)
- end
- end
-
- def test_should_cache_get_with_ok_status
- get :ok
- assert_response :ok
- assert_page_cached :ok, "get with ok status should have been cached"
- end
-
- def test_should_cache_with_custom_path
- get :custom_path
- assert File.exist?("#{FILE_STORE_PATH}/index.html")
- end
-
- def test_should_expire_cache_with_custom_path
- get :custom_path
- assert File.exist?("#{FILE_STORE_PATH}/index.html")
-
- get :expire_custom_path
- assert !File.exist?("#{FILE_STORE_PATH}/index.html")
- end
-
- def test_should_gzip_cache
- get :custom_path
- assert File.exist?("#{FILE_STORE_PATH}/index.html.gz")
-
- get :expire_custom_path
- assert !File.exist?("#{FILE_STORE_PATH}/index.html.gz")
- end
-
- def test_should_allow_to_disable_gzip
- get :no_gzip
- assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/no_gzip.html")
- assert !File.exist?("#{FILE_STORE_PATH}/page_caching_test/no_gzip.html.gz")
- end
-
- def test_should_use_config_gzip_by_default
- @controller.expects(:cache_page).with(nil, nil, Zlib::BEST_COMPRESSION)
- get :default_gzip
- end
-
- def test_should_set_gzip_level
- @controller.expects(:cache_page).with(nil, nil, Zlib::BEST_SPEED)
- get :gzip_level
- end
-
- def test_should_cache_without_trailing_slash_on_url
- @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash'
- assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
- end
-
- def test_should_obey_http_accept_attribute
- @request.env['HTTP_ACCEPT'] = 'text/xml'
- get :about_me
- assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/about_me.xml")
- assert_equal 'I am xml', @response.body
- end
-
- def test_cached_page_should_not_have_trailing_slash_even_if_url_has_trailing_slash
- @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash/'
- assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
- end
-
- def test_should_cache_ok_at_custom_path
- @request.env['PATH_INFO'] = '/index.html'
- get :ok
- assert_response :ok
- assert File.exist?("#{FILE_STORE_PATH}/index.html")
- end
-
- [:ok, :no_content, :found, :not_found].each do |status|
- [:get, :post, :patch, :put, :delete].each do |method|
- unless method == :get && status == :ok
- define_method "test_shouldnt_cache_#{method}_with_#{status}_status" do
- send(method, status)
- assert_response status
- assert_page_not_cached status, "#{method} with #{status} status shouldn't have been cached"
- end
- end
- end
- end
-
- def test_page_caching_conditional_options
- get :ok, :format=>'json'
- assert_page_not_cached :ok
- end
-
- def test_page_caching_directory_set_as_pathname
- begin
- ActionController::Base.page_cache_directory = Pathname.new(FILE_STORE_PATH)
- get :ok
- assert_response :ok
- assert_page_cached :ok
- ensure
- ActionController::Base.page_cache_directory = FILE_STORE_PATH
- end
- end
-
- private
- def assert_page_cached(action, message = "#{action} should have been cached")
- assert page_cached?(action), message
- end
-
- def assert_page_not_cached(action, message = "#{action} shouldn't have been cached")
- assert !page_cached?(action), message
- end
-
- def page_cached?(action)
- File.exist? "#{FILE_STORE_PATH}/page_caching_test/#{action}.html"
- end
-end
-
-class ActionCachingTestController < CachingController
- rescue_from(Exception) { head 500 }
- rescue_from(ActionController::UnknownFormat) { head :not_acceptable }
- if defined? ActiveRecord
- rescue_from(ActiveRecord::RecordNotFound) { head :not_found }
- end
-
- # Eliminate uninitialized ivar warning
- before_filter { @title = nil }
-
- caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| c.request.format && !c.request.format.json? }, :expires_in => 1.hour
- caches_action :show, :cache_path => 'http://test.host/custom/show'
- caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" }
- caches_action :with_layout
- caches_action :with_format_and_http_param, :cache_path => Proc.new { |c| { :key => 'value' } }
- caches_action :layout_false, :layout => false
- caches_action :with_layout_proc_param, :layout => Proc.new { |c| c.params[:layout] }
- caches_action :record_not_found, :four_oh_four, :simple_runtime_error
- caches_action :streaming
- caches_action :invalid
-
- layout 'talk_from_action'
-
- def index
- @cache_this = MockTime.now.to_f.to_s
- render :text => @cache_this
- end
-
- def redirected
- redirect_to :action => 'index'
- end
-
- def forbidden
- render :text => "Forbidden"
- response.status = "403 Forbidden"
- end
-
- def with_layout
- @cache_this = MockTime.now.to_f.to_s
- @title = nil
- render :text => @cache_this, :layout => true
- end
-
- def with_format_and_http_param
- @cache_this = MockTime.now.to_f.to_s
- render :text => @cache_this
- end
-
- def record_not_found
- raise ActiveRecord::RecordNotFound, "oops!"
- end
-
- def four_oh_four
- render :text => "404'd!", :status => 404
- end
-
- def simple_runtime_error
- raise "oops!"
- end
-
- alias_method :show, :index
- alias_method :edit, :index
- alias_method :destroy, :index
- alias_method :layout_false, :with_layout
- alias_method :with_layout_proc_param, :with_layout
-
- def expire
- expire_action :controller => 'action_caching_test', :action => 'index'
- render :nothing => true
- end
-
- def expire_xml
- expire_action :controller => 'action_caching_test', :action => 'index', :format => 'xml'
- render :nothing => true
- end
-
- def expire_with_url_string
- expire_action url_for(:controller => 'action_caching_test', :action => 'index')
- render :nothing => true
- end
-
- def streaming
- render :text => "streaming", :stream => true
- end
-
- def invalid
- @cache_this = MockTime.now.to_f.to_s
-
- respond_to do |format|
- format.json{ render :json => @cache_this }
- end
- end
-end
-
-class MockTime < Time
- # Let Time spicy to assure that Time.now != Time.now
- def to_f
- super+rand
- end
-end
-
-class ActionCachingMockController
- attr_accessor :mock_url_for
- attr_accessor :mock_path
-
- def initialize
- yield self if block_given?
- end
-
- def url_for(*args)
- @mock_url_for
- end
-
- def params
- request.parameters
- end
-
- def request
- Object.new.instance_eval(<<-EVAL)
- def path; '#{@mock_path}' end
- def format; 'all' end
- def parameters; {:format => nil}; end
- self
- EVAL
- end
-end
-
-class ActionCacheTest < ActionController::TestCase
- tests ActionCachingTestController
-
- def setup
- super
- @request.host = 'hostname.com'
- FileUtils.mkdir_p(FILE_STORE_PATH)
- @path_class = ActionController::Caching::Actions::ActionCachePath
- @mock_controller = ActionCachingMockController.new
- end
-
- def teardown
- super
- FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
- end
-
- def test_simple_action_cache
- get :index
- assert_response :success
- cached_time = content_to_cache
- assert_equal cached_time, @response.body
- assert fragment_exist?('hostname.com/action_caching_test')
-
- get :index
- assert_response :success
- assert_equal cached_time, @response.body
- end
-
- def test_simple_action_not_cached
- get :destroy
- assert_response :success
- cached_time = content_to_cache
- assert_equal cached_time, @response.body
- assert !fragment_exist?('hostname.com/action_caching_test/destroy')
-
- get :destroy
- assert_response :success
- assert_not_equal cached_time, @response.body
- end
-
- include RackTestUtils
-
- def test_action_cache_with_layout
- get :with_layout
- assert_response :success
- cached_time = content_to_cache
- assert_not_equal cached_time, @response.body
- assert fragment_exist?('hostname.com/action_caching_test/with_layout')
-
- get :with_layout
- assert_response :success
- assert_not_equal cached_time, @response.body
- body = body_to_string(read_fragment('hostname.com/action_caching_test/with_layout'))
- assert_equal @response.body, body
- end
-
- def test_action_cache_with_layout_and_layout_cache_false
- get :layout_false
- assert_response :success
- cached_time = content_to_cache
- assert_not_equal cached_time, @response.body
- assert fragment_exist?('hostname.com/action_caching_test/layout_false')
-
- get :layout_false
- assert_response :success
- assert_not_equal cached_time, @response.body
- body = body_to_string(read_fragment('hostname.com/action_caching_test/layout_false'))
- assert_equal cached_time, body
- end
-
- def test_action_cache_with_layout_and_layout_cache_false_via_proc
- get :with_layout_proc_param, :layout => false
- assert_response :success
- cached_time = content_to_cache
- assert_not_equal cached_time, @response.body
- assert fragment_exist?('hostname.com/action_caching_test/with_layout_proc_param')
-
- get :with_layout_proc_param, :layout => false
- assert_response :success
- assert_not_equal cached_time, @response.body
- body = body_to_string(read_fragment('hostname.com/action_caching_test/with_layout_proc_param'))
- assert_equal cached_time, body
- end
-
- def test_action_cache_with_layout_and_layout_cache_true_via_proc
- get :with_layout_proc_param, :layout => true
- assert_response :success
- cached_time = content_to_cache
- assert_not_equal cached_time, @response.body
- assert fragment_exist?('hostname.com/action_caching_test/with_layout_proc_param')
-
- get :with_layout_proc_param, :layout => true
- assert_response :success
- assert_not_equal cached_time, @response.body
- body = body_to_string(read_fragment('hostname.com/action_caching_test/with_layout_proc_param'))
- assert_equal @response.body, body
- end
-
- def test_action_cache_conditional_options
- @request.env['HTTP_ACCEPT'] = 'application/json'
- get :index
- assert_response :success
- assert !fragment_exist?('hostname.com/action_caching_test')
- end
-
- def test_action_cache_with_format_and_http_param
- get :with_format_and_http_param, :format => 'json'
- assert_response :success
- assert !fragment_exist?('hostname.com/action_caching_test/with_format_and_http_param.json?key=value.json')
- assert fragment_exist?('hostname.com/action_caching_test/with_format_and_http_param.json?key=value')
- end
-
- def test_action_cache_with_store_options
- MockTime.expects(:now).returns(12345).once
- @controller.expects(:read_fragment).with('hostname.com/action_caching_test', :expires_in => 1.hour).once
- @controller.expects(:write_fragment).with('hostname.com/action_caching_test', '12345.0', :expires_in => 1.hour).once
- get :index
- assert_response :success
- end
-
- def test_action_cache_with_custom_cache_path
- get :show
- assert_response :success
- cached_time = content_to_cache
- assert_equal cached_time, @response.body
- assert fragment_exist?('test.host/custom/show')
-
- get :show
- assert_response :success
- assert_equal cached_time, @response.body
- end
-
- def test_action_cache_with_custom_cache_path_in_block
- get :edit
- assert_response :success
- assert fragment_exist?('test.host/edit')
-
- get :edit, :id => 1
- assert_response :success
- assert fragment_exist?('test.host/1;edit')
- end
-
- def test_cache_expiration
- get :index
- assert_response :success
- cached_time = content_to_cache
-
- get :index
- assert_response :success
- assert_equal cached_time, @response.body
-
- get :expire
- assert_response :success
-
- get :index
- assert_response :success
- new_cached_time = content_to_cache
- assert_not_equal cached_time, @response.body
-
- get :index
- assert_response :success
- assert_equal new_cached_time, @response.body
- end
-
- def test_cache_expiration_isnt_affected_by_request_format
- get :index
- cached_time = content_to_cache
-
- @request.request_uri = "/action_caching_test/expire.xml"
- get :expire, :format => :xml
- assert_response :success
-
- get :index
- assert_response :success
- assert_not_equal cached_time, @response.body
- end
-
- def test_cache_expiration_with_url_string
- get :index
- cached_time = content_to_cache
-
- @request.request_uri = "/action_caching_test/expire_with_url_string"
- get :expire_with_url_string
- assert_response :success
-
- get :index
- assert_response :success
- assert_not_equal cached_time, @response.body
- end
-
- def test_cache_is_scoped_by_subdomain
- @request.host = 'jamis.hostname.com'
- get :index
- assert_response :success
- jamis_cache = content_to_cache
-
- @request.host = 'david.hostname.com'
- get :index
- assert_response :success
- david_cache = content_to_cache
- assert_not_equal jamis_cache, @response.body
-
- @request.host = 'jamis.hostname.com'
- get :index
- assert_response :success
- assert_equal jamis_cache, @response.body
-
- @request.host = 'david.hostname.com'
- get :index
- assert_response :success
- assert_equal david_cache, @response.body
- end
-
- def test_redirect_is_not_cached
- get :redirected
- assert_response :redirect
- get :redirected
- assert_response :redirect
- end
-
- def test_forbidden_is_not_cached
- get :forbidden
- assert_response :forbidden
- get :forbidden
- assert_response :forbidden
- end
-
- def test_xml_version_of_resource_is_treated_as_different_cache
- with_routing do |set|
- set.draw do
- get ':controller(/:action(.:format))'
- end
-
- get :index, :format => 'xml'
- assert_response :success
- cached_time = content_to_cache
- assert_equal cached_time, @response.body
- assert fragment_exist?('hostname.com/action_caching_test/index.xml')
-
- get :index, :format => 'xml'
- assert_response :success
- assert_equal cached_time, @response.body
- assert_equal 'application/xml', @response.content_type
-
- get :expire_xml
- assert_response :success
-
- get :index, :format => 'xml'
- assert_response :success
- assert_not_equal cached_time, @response.body
- end
- end
-
- def test_correct_content_type_is_returned_for_cache_hit
- # run it twice to cache it the first time
- get :index, :id => 'content-type', :format => 'xml'
- get :index, :id => 'content-type', :format => 'xml'
- assert_response :success
- assert_equal 'application/xml', @response.content_type
- end
-
- def test_correct_content_type_is_returned_for_cache_hit_on_action_with_string_key
- # run it twice to cache it the first time
- get :show, :format => 'xml'
- get :show, :format => 'xml'
- assert_response :success
- assert_equal 'application/xml', @response.content_type
- end
-
- def test_correct_content_type_is_returned_for_cache_hit_on_action_with_string_key_from_proc
- # run it twice to cache it the first time
- get :edit, :id => 1, :format => 'xml'
- get :edit, :id => 1, :format => 'xml'
- assert_response :success
- assert_equal 'application/xml', @response.content_type
- end
-
- def test_empty_path_is_normalized
- @mock_controller.mock_url_for = 'http://example.org/'
- @mock_controller.mock_path = '/'
-
- assert_equal 'example.org/index', @path_class.new(@mock_controller, {}).path
- end
-
- def test_file_extensions
- get :index, :id => 'kitten.jpg'
- get :index, :id => 'kitten.jpg'
-
- assert_response :success
- end
-
- if defined? ActiveRecord
- def test_record_not_found_returns_404_for_multiple_requests
- get :record_not_found
- assert_response 404
- get :record_not_found
- assert_response 404
- end
- end
-
- def test_four_oh_four_returns_404_for_multiple_requests
- get :four_oh_four
- assert_response 404
- get :four_oh_four
- assert_response 404
- end
-
- def test_four_oh_four_renders_content
- get :four_oh_four
- assert_equal "404'd!", @response.body
- end
-
- def test_simple_runtime_error_returns_500_for_multiple_requests
- get :simple_runtime_error
- assert_response 500
- get :simple_runtime_error
- assert_response 500
- end
-
- def test_action_caching_plus_streaming
- get :streaming
- assert_response :success
- assert_match(/streaming/, @response.body)
- assert fragment_exist?('hostname.com/action_caching_test/streaming')
- end
-
- def test_invalid_format_returns_not_acceptable
- get :invalid, :format => "json"
- assert_response :success
- cached_time = content_to_cache
- assert_equal cached_time, @response.body
-
- assert fragment_exist?("hostname.com/action_caching_test/invalid.json")
-
- get :invalid, :format => "json"
- assert_response :success
- assert_equal cached_time, @response.body
-
- get :invalid, :format => "xml"
- assert_response :not_acceptable
-
- get :invalid, :format => "\xC3\x83"
- assert_response :not_acceptable
- end
-
- private
- def content_to_cache
- assigns(:cache_this)
- end
-
- def fragment_exist?(path)
- @controller.fragment_exist?(path)
- end
-
- def read_fragment(path)
- @controller.read_fragment(path)
- end
-end
-
class FragmentCachingTestController < CachingController
def some_action; end;
end
@@ -988,3 +284,17 @@ class CacheHelperOutputBufferTest < ActionController::TestCase
end
end
+class DeprecatedPageCacheExtensionTest < ActiveSupport::TestCase
+ def test_page_cache_extension_binds_default_static_extension
+ deprecation_behavior = ActiveSupport::Deprecation.behavior
+ ActiveSupport::Deprecation.behavior = :silence
+ old_extension = ActionController::Base.default_static_extension
+
+ ActionController::Base.page_cache_extension = '.rss'
+
+ assert_equal '.rss', ActionController::Base.default_static_extension
+ ensure
+ ActiveSupport::Deprecation.behavior = deprecation_behavior
+ ActionController::Base.default_static_extension = old_extension
+ end
+end
diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb
index a72b6dde1a..9efb6ab95f 100644
--- a/actionpack/test/controller/log_subscriber_test.rb
+++ b/actionpack/test/controller/log_subscriber_test.rb
@@ -42,11 +42,6 @@ module Another
render :inline => "<%= cache('foo%bar'){ 'Contains % sign in key' } %>"
end
- def with_page_cache
- cache_page("Super soaker", "/index.html")
- render :nothing => true
- end
-
def with_exception
raise Exception
end
@@ -71,7 +66,6 @@ class ACLogSubscriberTest < ActionController::TestCase
@old_logger = ActionController::Base.logger
@cache_path = File.expand_path('../temp/test_cache', File.dirname(__FILE__))
- ActionController::Base.page_cache_directory = @cache_path
@controller.cache_store = :file_store, @cache_path
ActionController::LogSubscriber.attach_to :action_controller
end
@@ -199,18 +193,6 @@ class ACLogSubscriberTest < ActionController::TestCase
@controller.config.perform_caching = true
end
- def test_with_page_cache
- @controller.config.perform_caching = true
- get :with_page_cache
- wait
-
- assert_equal 3, logs.size
- assert_match(/Write page/, logs[1])
- assert_match(/\/index\.html/, logs[1])
- ensure
- @controller.config.perform_caching = true
- end
-
def test_process_action_with_exception_includes_http_status_code
begin
get :with_exception
diff --git a/actionpack/test/controller/parameters/multi_parameter_attributes_test.rb b/actionpack/test/controller/parameters/multi_parameter_attributes_test.rb
new file mode 100644
index 0000000000..15338059bc
--- /dev/null
+++ b/actionpack/test/controller/parameters/multi_parameter_attributes_test.rb
@@ -0,0 +1,38 @@
+require 'abstract_unit'
+require 'action_controller/metal/strong_parameters'
+
+class MultiParameterAttributesTest < ActiveSupport::TestCase
+ test "permitted multi-parameter attribute keys" do
+ params = ActionController::Parameters.new({
+ book: {
+ "shipped_at(1i)" => "2012",
+ "shipped_at(2i)" => "3",
+ "shipped_at(3i)" => "25",
+ "shipped_at(4i)" => "10",
+ "shipped_at(5i)" => "15",
+ "published_at(1i)" => "1999",
+ "published_at(2i)" => "2",
+ "published_at(3i)" => "5",
+ "price(1)" => "R$",
+ "price(2f)" => "2.02"
+ }
+ })
+
+ permitted = params.permit book: [ :shipped_at, :price ]
+
+ assert permitted.permitted?
+
+ assert_equal "2012", permitted[:book]["shipped_at(1i)"]
+ assert_equal "3", permitted[:book]["shipped_at(2i)"]
+ assert_equal "25", permitted[:book]["shipped_at(3i)"]
+ assert_equal "10", permitted[:book]["shipped_at(4i)"]
+ assert_equal "15", permitted[:book]["shipped_at(5i)"]
+
+ assert_equal "R$", permitted[:book]["price(1)"]
+ assert_equal "2.02", permitted[:book]["price(2f)"]
+
+ assert_nil permitted[:book]["published_at(1i)"]
+ assert_nil permitted[:book]["published_at(2i)"]
+ assert_nil permitted[:book]["published_at(3i)"]
+ end
+end
diff --git a/actionpack/test/controller/parameters/nested_parameters_test.rb b/actionpack/test/controller/parameters/nested_parameters_test.rb
index 41f5b6e127..d287e79cba 100644
--- a/actionpack/test/controller/parameters/nested_parameters_test.rb
+++ b/actionpack/test/controller/parameters/nested_parameters_test.rb
@@ -15,18 +15,22 @@ class NestedParametersTest < ActiveSupport::TestCase
details: {
pages: 200,
genre: "Tragedy"
+ },
+ id: {
+ isbn: 'x'
}
},
magazine: "Mjallo!"
})
- permitted = params.permit book: [ :title, { authors: [ :name ] }, { details: :pages } ]
+ permitted = params.permit book: [ :title, { authors: [ :name ] }, { details: :pages }, :id ]
assert permitted.permitted?
assert_equal "Romeo and Juliet", permitted[:book][:title]
assert_equal "William Shakespeare", permitted[:book][:authors][0][:name]
assert_equal "Christopher Marlowe", permitted[:book][:authors][1][:name]
assert_equal 200, permitted[:book][:details][:pages]
+ assert_nil permitted[:book][:id]
assert_nil permitted[:book][:details][:genre]
assert_nil permitted[:book][:authors][0][:born]
assert_nil permitted[:magazine]
diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb
index 7fe8e6051b..ad970f0a9a 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -3,7 +3,7 @@ require 'action_controller/metal/strong_parameters'
class ParametersPermitTest < ActiveSupport::TestCase
setup do
- @params = ActionController::Parameters.new({ person: {
+ @params = ActionController::Parameters.new({ person: {
age: "32", name: { first: "David", last: "Heinemeier Hansson" }
}})
end
@@ -57,6 +57,13 @@ class ParametersPermitTest < ActiveSupport::TestCase
assert_equal @params.permitted?, @params.dup.permitted?
end
+ test "permit is recursive" do
+ @params.permit!
+ assert @params.permitted?
+ assert @params[:person].permitted?
+ assert @params[:person][:name].permitted?
+ end
+
test "permitted takes a default value when Parameters.permit_all_parameters is set" do
begin
ActionController::Parameters.permit_all_parameters = true
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index fd8f87e377..aa33f01d02 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -1428,6 +1428,17 @@ class RenderTest < ActionController::TestCase
assert_equal "Bonjour: davidBonjour: mary", @response.body
end
+ def test_locals_option_to_assert_template_is_not_supported
+ warning_buffer = StringIO.new
+ $stderr = warning_buffer
+
+ get :partial_collection_with_locals
+ 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
diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb
index 351b9c4cfa..ab1bd0e3b6 100644
--- a/actionpack/test/controller/show_exceptions_test.rb
+++ b/actionpack/test/controller/show_exceptions_test.rb
@@ -93,4 +93,20 @@ module ShowExceptions
assert_equal 'text/html', response.content_type.to_s
end
end
+
+ class ShowFailsafeExceptionsTest < ActionDispatch::IntegrationTest
+ def test_render_failsafe_exception
+ @app = ShowExceptionsOverridenController.action(:boom)
+ @exceptions_app = @app.instance_variable_get(:@exceptions_app)
+ @app.instance_variable_set(:@exceptions_app, nil)
+ $stderr = StringIO.new
+
+ get '/', {}, 'HTTP_ACCEPT' => 'text/json'
+ assert_response :internal_server_error
+ assert_equal 'text/plain', response.content_type.to_s
+
+ @app.instance_variable_set(:@exceptions_app, @exceptions_app)
+ $stderr = STDERR
+ end
+ end
end
diff --git a/actionpack/test/dispatch/header_test.rb b/actionpack/test/dispatch/header_test.rb
index bc7cad8db5..42432510c3 100644
--- a/actionpack/test/dispatch/header_test.rb
+++ b/actionpack/test/dispatch/header_test.rb
@@ -7,6 +7,26 @@ class HeaderTest < ActiveSupport::TestCase
)
end
+ def test_each
+ headers = []
+ @headers.each { |pair| headers << pair }
+ assert_equal [["HTTP_CONTENT_TYPE", "text/plain"]], headers
+ end
+
+ def test_setter
+ @headers['foo'] = "bar"
+ assert_equal "bar", @headers['foo']
+ end
+
+ def test_key?
+ assert @headers.key?('HTTP_CONTENT_TYPE')
+ assert @headers.include?('HTTP_CONTENT_TYPE')
+ end
+
+ def test_fetch_with_block
+ assert_equal 'omg', @headers.fetch('notthere') { 'omg' }
+ end
+
test "content type" do
assert_equal "text/plain", @headers["Content-Type"]
assert_equal "text/plain", @headers["content-type"]
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 4e83ad16d7..93d89f7568 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -1124,6 +1124,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/sheep/1/_it', _it_sheep_path(1)
end
+ def test_resource_does_not_modify_passed_options
+ options = {:id => /.+?/, :format => /json|xml/}
+ self.class.stub_controllers do |routes|
+ routes.draw do
+ resource :user, options
+ end
+ end
+ assert_equal({:id => /.+?/, :format => /json|xml/}, options)
+ end
+
+ def test_resources_does_not_modify_passed_options
+ options = {:id => /.+?/, :format => /json|xml/}
+ self.class.stub_controllers do |routes|
+ routes.draw do
+ resources :users, options
+ end
+ end
+ assert_equal({:id => /.+?/, :format => /json|xml/}, options)
+ end
+
def test_path_names
get '/pt/projetos'
assert_equal 'projects#index', @response.body
diff --git a/actionpack/test/dispatch/session/cache_store_test.rb b/actionpack/test/dispatch/session/cache_store_test.rb
index a74e165826..b8479e8836 100644
--- a/actionpack/test/dispatch/session/cache_store_test.rb
+++ b/actionpack/test/dispatch/session/cache_store_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'fixtures/session_autoload_test/session_autoload_test/foo'
class CacheStoreTest < ActionDispatch::IntegrationTest
class TestController < ActionController::Base
diff --git a/actionpack/test/fixtures/digestor/level/below/_header.html.erb b/actionpack/test/fixtures/digestor/level/below/_header.html.erb
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/digestor/level/below/_header.html.erb
diff --git a/actionpack/test/fixtures/digestor/level/below/index.html.erb b/actionpack/test/fixtures/digestor/level/below/index.html.erb
new file mode 100644
index 0000000000..b92f49a8f8
--- /dev/null
+++ b/actionpack/test/fixtures/digestor/level/below/index.html.erb
@@ -0,0 +1 @@
+<%= render partial: "header" %>
diff --git a/actionpack/test/fixtures/test/render_two_partials.html.erb b/actionpack/test/fixtures/test/render_two_partials.html.erb
new file mode 100644
index 0000000000..3db6025860
--- /dev/null
+++ b/actionpack/test/fixtures/test/render_two_partials.html.erb
@@ -0,0 +1,2 @@
+<%= render :partial => 'partial', :locals => {'first' => '1'} %>
+<%= render :partial => 'partial', :locals => {'second' => '2'} %>
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index a04694714d..eb1a54a81f 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -13,27 +13,10 @@ end
class AssetTagHelperTest < ActionView::TestCase
tests ActionView::Helpers::AssetTagHelper
+ attr_reader :request
+
def setup
super
- silence_warnings do
- ActionView::Helpers::AssetTagHelper.send(
- :const_set,
- :JAVASCRIPTS_DIR,
- File.dirname(__FILE__) + "/../fixtures/public/javascripts"
- )
-
- ActionView::Helpers::AssetTagHelper.send(
- :const_set,
- :STYLESHEETS_DIR,
- File.dirname(__FILE__) + "/../fixtures/public/stylesheets"
- )
-
- ActionView::Helpers::AssetTagHelper.send(
- :const_set,
- :ASSETS_DIR,
- File.dirname(__FILE__) + "/../fixtures/public"
- )
- end
@controller = BasicController.new
@@ -42,24 +25,33 @@ class AssetTagHelperTest < ActionView::TestCase
def protocol() 'http://' end
def ssl?() false end
def host_with_port() 'localhost' end
+ def base_url() 'http://www.example.com' end
end.new
@controller.request = @request
-
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :defaults => ['prototype', 'effects', 'dragdrop', 'controls', 'rails']
end
def url_for(*args)
"http://www.example.com"
end
- def teardown
- config.perform_caching = false
- ENV.delete('RAILS_ASSET_ID')
+ AssetPathToTag = {
+ %(asset_path("foo")) => %(/foo),
+ %(asset_path("style.css")) => %(/style.css),
+ %(asset_path("xmlhr.js")) => %(/xmlhr.js),
+ %(asset_path("xml.png")) => %(/xml.png),
+ %(asset_path("dir/xml.png")) => %(/dir/xml.png),
+ %(asset_path("/dir/xml.png")) => %(/dir/xml.png),
- JavascriptIncludeTag.expansions.clear
- StylesheetIncludeTag.expansions.clear
- end
+ %(asset_path("script.min")) => %(/script.min),
+ %(asset_path("script.min.js")) => %(/script.min.js),
+ %(asset_path("style.min")) => %(/style.min),
+ %(asset_path("style.min.css")) => %(/style.min.css),
+
+ %(asset_path("style", type: :stylesheet)) => %(/stylesheets/style.css),
+ %(asset_path("xmlhr", type: :javascript)) => %(/javascripts/xmlhr.js),
+ %(asset_path("xml.png", type: :image)) => %(/images/xml.png)
+ }
AutoDiscoveryToTag = {
%(auto_discovery_link_tag) => %(<link href="http://www.example.com" rel="alternate" title="RSS" type="application/rss+xml" />),
@@ -79,7 +71,14 @@ class AssetTagHelperTest < ActionView::TestCase
JavascriptPathToTag = {
%(javascript_path("xmlhr")) => %(/javascripts/xmlhr.js),
%(javascript_path("super/xmlhr")) => %(/javascripts/super/xmlhr.js),
- %(javascript_path("/super/xmlhr.js")) => %(/super/xmlhr.js)
+ %(javascript_path("/super/xmlhr.js")) => %(/super/xmlhr.js),
+ %(javascript_path("xmlhr.min")) => %(/javascripts/xmlhr.min.js),
+ %(javascript_path("xmlhr.min.js")) => %(/javascripts/xmlhr.min.js),
+
+ %(javascript_path("xmlhr.js?123")) => %(/javascripts/xmlhr.js?123),
+ %(javascript_path("xmlhr.js?body=1")) => %(/javascripts/xmlhr.js?body=1),
+ %(javascript_path("xmlhr.js#hash")) => %(/javascripts/xmlhr.js#hash),
+ %(javascript_path("xmlhr.js?123#hash")) => %(/javascripts/xmlhr.js?123#hash)
}
PathToJavascriptToTag = {
@@ -104,13 +103,6 @@ class AssetTagHelperTest < ActionView::TestCase
%(javascript_include_tag("bank")) => %(<script src="/javascripts/bank.js" ></script>),
%(javascript_include_tag("bank.js")) => %(<script src="/javascripts/bank.js" ></script>),
%(javascript_include_tag("bank", :lang => "vbscript")) => %(<script lang="vbscript" src="/javascripts/bank.js" ></script>),
- %(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(<script src="/javascripts/common.javascript" ></script>\n<script src="/elsewhere/cools.js" ></script>),
- %(javascript_include_tag(:defaults)) => %(<script src="/javascripts/prototype.js" ></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>),
- %(javascript_include_tag(:all)) => %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/version.1.0.js"></script>\n<script src="/javascripts/application.js"></script>),
- %(javascript_include_tag(:all, :recursive => true)) => %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/subdir/subdir.js"></script>\n<script src="/javascripts/version.1.0.js"></script>\n<script src="/javascripts/application.js"></script>),
- %(javascript_include_tag(:defaults, "bank")) => %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/application.js"></script>),
- %(javascript_include_tag(:defaults, "application")) => %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>),
- %(javascript_include_tag("bank", :defaults)) => %(<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>),
%(javascript_include_tag("http://example.com/all")) => %(<script src="http://example.com/all"></script>),
%(javascript_include_tag("http://example.com/all.js")) => %(<script src="http://example.com/all.js"></script>),
@@ -121,14 +113,17 @@ class AssetTagHelperTest < ActionView::TestCase
%(stylesheet_path("bank")) => %(/stylesheets/bank.css),
%(stylesheet_path("bank.css")) => %(/stylesheets/bank.css),
%(stylesheet_path('subdir/subdir')) => %(/stylesheets/subdir/subdir.css),
- %(stylesheet_path('/subdir/subdir.css')) => %(/subdir/subdir.css)
+ %(stylesheet_path('/subdir/subdir.css')) => %(/subdir/subdir.css),
+ %(stylesheet_path("style.min")) => %(/stylesheets/style.min.css),
+ %(stylesheet_path("style.min.css")) => %(/stylesheets/style.min.css)
}
PathToStyleToTag = {
%(path_to_stylesheet("style")) => %(/stylesheets/style.css),
%(path_to_stylesheet("style.css")) => %(/stylesheets/style.css),
%(path_to_stylesheet('dir/file')) => %(/stylesheets/dir/file.css),
- %(path_to_stylesheet('/dir/file.rcss')) => %(/dir/file.rcss)
+ %(path_to_stylesheet('/dir/file.rcss', :extname => false)) => %(/dir/file.rcss),
+ %(path_to_stylesheet('/dir/file', :extname => '.rcss')) => %(/dir/file.rcss)
}
StyleUrlToTag = {
@@ -142,7 +137,8 @@ class AssetTagHelperTest < ActionView::TestCase
%(url_to_stylesheet("style")) => %(http://www.example.com/stylesheets/style.css),
%(url_to_stylesheet("style.css")) => %(http://www.example.com/stylesheets/style.css),
%(url_to_stylesheet('dir/file')) => %(http://www.example.com/stylesheets/dir/file.css),
- %(url_to_stylesheet('/dir/file.rcss')) => %(http://www.example.com/dir/file.rcss)
+ %(url_to_stylesheet('/dir/file.rcss', :extname => false)) => %(http://www.example.com/dir/file.rcss),
+ %(url_to_stylesheet('/dir/file', :extname => '.rcss')) => %(http://www.example.com/dir/file.rcss)
}
StyleLinkToTag = {
@@ -151,10 +147,6 @@ class AssetTagHelperTest < ActionView::TestCase
%(stylesheet_link_tag("/elsewhere/file")) => %(<link href="/elsewhere/file.css" media="screen" rel="stylesheet" />),
%(stylesheet_link_tag("subdir/subdir")) => %(<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" />),
%(stylesheet_link_tag("bank", :media => "all")) => %(<link href="/stylesheets/bank.css" media="all" rel="stylesheet" />),
- %(stylesheet_link_tag(:all)) => %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />),
- %(stylesheet_link_tag(:all, :recursive => true)) => %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />),
- %(stylesheet_link_tag(:all, :media => "all")) => %(<link href="/stylesheets/bank.css" media="all" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="all" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="all" rel="stylesheet" />),
- %(stylesheet_link_tag("random.styles", "/elsewhere/file")) => %(<link href="/stylesheets/random.styles" media="screen" rel="stylesheet" />\n<link href="/elsewhere/file.css" media="screen" rel="stylesheet" />),
%(stylesheet_link_tag("http://www.example.com/styles/style")) => %(<link href="http://www.example.com/styles/style" media="screen" rel="stylesheet" />),
%(stylesheet_link_tag("http://www.example.com/styles/style.css")) => %(<link href="http://www.example.com/styles/style.css" media="screen" rel="stylesheet" />),
@@ -298,6 +290,14 @@ class AssetTagHelperTest < ActionView::TestCase
%(audio_tag(["audio.mp3", "audio.ogg"], :autobuffer => true, :controls => true)) => %(<audio autobuffer="autobuffer" controls="controls"><source src="/audios/audio.mp3" /><source src="/audios/audio.ogg" /></audio>)
}
+ FontPathToTag = {
+ %(font_path("font.eot")) => %(/fonts/font.eot),
+ %(font_path("font.eot#iefix")) => %(/fonts/font.eot#iefix),
+ %(font_path("font.woff")) => %(/fonts/font.woff),
+ %(font_path("font.ttf")) => %(/fonts/font.ttf),
+ %(font_path("font.ttf?123")) => %(/fonts/font.ttf?123)
+ }
+
def test_autodiscovery_link_tag_deprecated_types
result = nil
assert_deprecated do
@@ -308,6 +308,18 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal expected, result
end
+ def test_asset_path_tag
+ AssetPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
+ def test_compute_asset_public_path
+ assert_equal "/robots.txt", compute_asset_path("robots.txt")
+ assert_equal "/robots.txt", compute_asset_path("/robots.txt")
+ assert_equal "/javascripts/foo.js", compute_asset_path("foo.js", :type => :javascript)
+ assert_equal "/javascripts/foo.js", compute_asset_path("/foo.js", :type => :javascript)
+ assert_equal "/stylesheets/foo.css", compute_asset_path("foo.css", :type => :stylesheet)
+ end
+
def test_auto_discovery_link_tag
AutoDiscoveryToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -328,8 +340,7 @@ class AssetTagHelperTest < ActionView::TestCase
UrlToJavascriptToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
- def test_javascript_include_tag_with_blank_asset_id
- ENV["RAILS_ASSET_ID"] = ""
+ def test_javascript_include_tag
JavascriptIncludeToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -339,104 +350,15 @@ class AssetTagHelperTest < ActionView::TestCase
}
assert_nothing_raised {
- javascript_include_tag(:defaults, 'missing_security_guard')
- }
-
- assert_nothing_raised {
javascript_include_tag('http://example.com/css/missing_security_guard')
}
end
- def test_javascript_include_tag_with_given_asset_id
- ENV["RAILS_ASSET_ID"] = "1"
- assert_dom_equal(%(<script src="/javascripts/prototype.js?1"></script>\n<script src="/javascripts/effects.js?1"></script>\n<script src="/javascripts/dragdrop.js?1"></script>\n<script src="/javascripts/controls.js?1"></script>\n<script src="/javascripts/rails.js?1"></script>\n<script src="/javascripts/application.js?1"></script>), javascript_include_tag(:defaults))
- end
-
def test_javascript_include_tag_is_html_safe
- assert javascript_include_tag(:defaults).html_safe?
assert javascript_include_tag("prototype").html_safe?
end
- def test_custom_javascript_expansions
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"]
- assert_dom_equal %(<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/effects.js"></script>), javascript_include_tag('controls', :robbery, 'effects')
- end
-
- def test_custom_javascript_expansions_return_unique_set
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :defaults => %w(prototype effects dragdrop controls rails application)
- assert_dom_equal %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>), javascript_include_tag(:defaults)
- end
-
- def test_custom_javascript_expansions_and_defaults_puts_application_js_at_the_end
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"]
- assert_dom_equal %(<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/application.js"></script>), javascript_include_tag('controls',:defaults, :robbery, 'effects')
- end
-
- def test_javascript_include_tag_should_not_output_the_same_asset_twice
- ENV["RAILS_ASSET_ID"] = ""
- assert_dom_equal %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>), javascript_include_tag('prototype', 'effects', :defaults)
- end
-
- def test_javascript_include_tag_should_not_output_the_same_expansion_twice
- ENV["RAILS_ASSET_ID"] = ""
- assert_dom_equal %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>), javascript_include_tag(:defaults, :defaults)
- end
-
- def test_single_javascript_asset_keys_should_take_precedence_over_expansions
- ENV["RAILS_ASSET_ID"] = ""
- assert_dom_equal %(<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/application.js"></script>), javascript_include_tag('controls', :defaults, 'effects')
- assert_dom_equal %(<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>), javascript_include_tag('controls', 'effects', :defaults)
- end
-
- def test_registering_javascript_expansions_merges_with_existing_expansions
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :can_merge => ['bank']
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :can_merge => ['robber']
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :can_merge => ['bank']
- assert_dom_equal %(<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>), javascript_include_tag(:can_merge)
- end
-
- def test_custom_javascript_expansions_with_undefined_symbol
- assert_raise(ArgumentError) { javascript_include_tag('first', :unknown, 'last') }
- end
-
- def test_custom_javascript_expansions_with_nil_value
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :monkey => nil
- assert_dom_equal %(<script src="/javascripts/first.js"></script>\n<script src="/javascripts/last.js"></script>), javascript_include_tag('first', :monkey, 'last')
- end
-
- def test_custom_javascript_expansions_with_empty_array_value
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :monkey => []
- assert_dom_equal %(<script src="/javascripts/first.js"></script>\n<script src="/javascripts/last.js"></script>), javascript_include_tag('first', :monkey, 'last')
- end
-
- def test_custom_javascript_and_stylesheet_expansion_with_same_name
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"]
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :robbery => ["money", "security"]
- assert_dom_equal %(<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/effects.js"></script>), javascript_include_tag('controls', :robbery, 'effects')
- assert_dom_equal %(<link href="/stylesheets/style.css" rel="stylesheet" media="screen" />\n<link href="/stylesheets/money.css" rel="stylesheet" media="screen" />\n<link href="/stylesheets/security.css" rel="stylesheet" media="screen" />\n<link href="/stylesheets/print.css" rel="stylesheet" media="screen" />), stylesheet_link_tag('style', :robbery, 'print')
- end
-
- def test_reset_javascript_expansions
- JavascriptIncludeTag.expansions.clear
- assert_raise(ArgumentError) { javascript_include_tag(:defaults) }
- end
-
- def test_all_javascript_expansion_not_include_application_js_if_not_exists
- FileUtils.mv(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.js'),
- File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.bak'))
- assert_no_match(/application\.js/, javascript_include_tag(:all))
- ensure
- FileUtils.mv(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.bak'),
- File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.js'))
- end
-
def test_stylesheet_path
- ENV["RAILS_ASSET_ID"] = ""
StylePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -445,7 +367,6 @@ class AssetTagHelperTest < ActionView::TestCase
end
def test_stylesheet_url
- ENV["RAILS_ASSET_ID"] = ""
StyleUrlToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -454,7 +375,6 @@ class AssetTagHelperTest < ActionView::TestCase
end
def test_stylesheet_link_tag
- ENV["RAILS_ASSET_ID"] = ""
StyleLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -469,7 +389,6 @@ class AssetTagHelperTest < ActionView::TestCase
end
def test_stylesheet_link_tag_is_html_safe
- ENV["RAILS_ASSET_ID"] = ""
assert stylesheet_link_tag('dir/file').html_safe?
assert stylesheet_link_tag('dir/other/file', 'dir/file2').html_safe?
end
@@ -478,57 +397,10 @@ class AssetTagHelperTest < ActionView::TestCase
assert_dom_equal %(<link href="/file.css" media="&lt;script&gt;" rel="stylesheet" />), stylesheet_link_tag('/file', :media => '<script>')
end
- def test_custom_stylesheet_expansions
- ENV["RAILS_ASSET_ID"] = ''
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :robbery => ["bank", "robber"]
- assert_dom_equal %(<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" />), stylesheet_link_tag('version.1.0', :robbery, 'subdir/subdir')
- end
-
- def test_custom_stylesheet_expansions_return_unique_set
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :cities => %w(wellington amsterdam london)
- assert_dom_equal %(<link href="/stylesheets/wellington.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/amsterdam.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/london.css" media="screen" rel="stylesheet" />), stylesheet_link_tag(:cities)
- end
-
def test_stylesheet_link_tag_should_not_output_the_same_asset_twice
- ENV["RAILS_ASSET_ID"] = ""
assert_dom_equal %(<link href="/stylesheets/wellington.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/amsterdam.css" media="screen" rel="stylesheet" />), stylesheet_link_tag('wellington', 'wellington', 'amsterdam')
end
- def test_stylesheet_link_tag_should_not_output_the_same_expansion_twice
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :cities => %w(wellington amsterdam london)
- assert_dom_equal %(<link href="/stylesheets/wellington.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/amsterdam.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/london.css" media="screen" rel="stylesheet" />), stylesheet_link_tag(:cities, :cities)
- end
-
- def test_single_stylesheet_asset_keys_should_take_precedence_over_expansions
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :cities => %w(wellington amsterdam london)
- assert_dom_equal %(<link href="/stylesheets/london.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/wellington.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/amsterdam.css" media="screen" rel="stylesheet" />), stylesheet_link_tag('london', :cities)
- end
-
- def test_custom_stylesheet_expansions_with_unknown_symbol
- assert_raise(ArgumentError) { stylesheet_link_tag('first', :unknown, 'last') }
- end
-
- def test_custom_stylesheet_expansions_with_nil_value
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :monkey => nil
- assert_dom_equal %(<link href="/stylesheets/first.css" rel="stylesheet" media="screen" />\n<link href="/stylesheets/last.css" rel="stylesheet" media="screen" />), stylesheet_link_tag('first', :monkey, 'last')
- end
-
- def test_custom_stylesheet_expansions_with_empty_array_value
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :monkey => []
- assert_dom_equal %(<link href="/stylesheets/first.css" rel="stylesheet" media="screen" />\n<link href="/stylesheets/last.css" rel="stylesheet" media="screen" />), stylesheet_link_tag('first', :monkey, 'last')
- end
-
- def test_registering_stylesheet_expansions_merges_with_existing_expansions
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :can_merge => ['bank']
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :can_merge => ['robber']
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :can_merge => ['bank']
- assert_dom_equal %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />), stylesheet_link_tag(:can_merge)
- end
-
def test_image_path
ImagePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -567,21 +439,6 @@ class AssetTagHelperTest < ActionView::TestCase
FaviconLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
- def test_image_tag_windows_behaviour
- old_asset_id, ENV["RAILS_ASSET_ID"] = ENV["RAILS_ASSET_ID"], "1"
- # This simulates the behavior of File#exist? on windows when testing a file ending in "."
- # If the file "rails.png" exists, windows will return true when asked if "rails.png." exists (notice trailing ".")
- # OS X, linux etc will return false in this case.
- File.stubs(:exist?).with('template/../fixtures/public/images/rails.png.').returns(true)
- assert_equal '<img alt="Rails" src="/images/rails.png?1" />', image_tag('rails.png')
- ensure
- if old_asset_id
- ENV["RAILS_ASSET_ID"] = old_asset_id
- else
- ENV.delete("RAILS_ASSET_ID")
- end
- end
-
def test_video_path
VideoPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -622,6 +479,10 @@ class AssetTagHelperTest < ActionView::TestCase
AudioLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_font_path
+ FontPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
def test_video_audio_tag_does_not_modify_options
options = {:autoplay => true}
video_tag('video', options)
@@ -630,27 +491,6 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal({:autoplay => true}, options)
end
- def test_timebased_asset_id
- expected_time = File.mtime(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).to_i.to_s
- assert_equal %(<img alt="Rails" src="/images/rails.png?#{expected_time}" />), image_tag("rails.png")
- end
-
- def test_string_asset_id
- @controller.config.asset_path = "/assets.v12345%s"
-
- expected_path = "/assets.v12345/images/rails.png"
- assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
- end
-
- def test_proc_asset_id
- @controller.config.asset_path = Proc.new do |asset_path|
- "/assets.v12345#{asset_path}"
- end
-
- expected_path = "/assets.v12345/images/rails.png"
- assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
- end
-
def test_image_tag_interpreting_email_cid_correctly
# An inline image has no need for an alt tag to be automatically generated from the cid:
assert_equal '<img src="cid:thi%25%25sis@acontentid" />', image_tag("cid:thi%25%25sis@acontentid")
@@ -660,37 +500,6 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal '<img alt="Image" src="cid:thi%25%25sis@acontentid" />', image_tag("cid:thi%25%25sis@acontentid", :alt => "Image")
end
- def test_timebased_asset_id_with_relative_url_root
- @controller.config.relative_url_root = "/collaboration/hieraki"
- expected_time = File.mtime(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).to_i.to_s
- assert_equal %(<img alt="Rails" src="#{@controller.config.relative_url_root}/images/rails.png?#{expected_time}" />), image_tag("rails.png")
- end
-
- # Same as above, but with script_name
- def test_timebased_asset_id_with_script_name
- @request.script_name = "/collaboration/hieraki"
- expected_time = File.mtime(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).to_i.to_s
- assert_equal %(<img alt="Rails" src="#{@request.script_name}/images/rails.png?#{expected_time}" />), image_tag("rails.png")
- end
-
- def test_should_skip_asset_id_on_complete_url
- assert_equal %(<img alt="Rails" src="http://www.example.com/rails.png" />), image_tag("http://www.example.com/rails.png")
- end
-
- def test_should_skip_asset_id_on_scheme_relative_url
- assert_equal %(<img alt="Rails" src="//www.example.com/rails.png" />), image_tag("//www.example.com/rails.png")
- end
-
- def test_should_use_preset_asset_id
- ENV["RAILS_ASSET_ID"] = "4500"
- assert_equal %(<img alt="Rails" src="/images/rails.png?4500" />), image_tag("rails.png")
- end
-
- def test_preset_empty_asset_id
- ENV["RAILS_ASSET_ID"] = ""
- assert_equal %(<img alt="Rails" src="/images/rails.png" />), image_tag("rails.png")
- end
-
def test_should_not_modify_source_string
source = '/images/rails.png'
copy = source.dup
@@ -699,7 +508,6 @@ class AssetTagHelperTest < ActionView::TestCase
end
def test_caching_image_path_with_caching_and_proc_asset_host_using_request
- ENV['RAILS_ASSET_ID'] = ''
@controller.config.asset_host = Proc.new do |source, request|
if request.ssl?
"#{request.protocol}#{request.host_with_port}"
@@ -714,630 +522,20 @@ class AssetTagHelperTest < ActionView::TestCase
@controller.request.stubs(:ssl?).returns(true)
assert_equal "http://localhost/images/xml.png", image_path("xml.png")
end
-
- def test_caching_javascript_include_tag_when_caching_on
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.asset_host = 'http://a0.example.com'
- config.perform_caching = true
-
- assert_dom_equal(
- %(<script src="http://a0.example.com/javascripts/all.js"></script>),
- javascript_include_tag(:all, :cache => true)
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_dom_equal(
- %(<script src="http://a0.example.com/javascripts/money.js"></script>),
- javascript_include_tag(:all, :cache => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
-
- assert_dom_equal(
- %(<script src="http://a0.example.com/absolute/test.js"></script>),
- javascript_include_tag(:all, :cache => "/absolute/test")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute', 'test.js'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute'))
- end
-
- def test_caching_javascript_include_tag_when_caching_on_with_proc_asset_host
- ENV['RAILS_ASSET_ID'] = ''
- @controller.config.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" }
- config.perform_caching = true
-
- assert_equal '/javascripts/scripts.js'.length, 23
- assert_dom_equal(
- %(<script src="http://a23.example.com/javascripts/scripts.js"></script>),
- javascript_include_tag(:all, :cache => 'scripts')
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'scripts.js'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'scripts.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_on_with_2_argument_proc_asset_host
- ENV['RAILS_ASSET_ID'] = ''
- @controller.config.asset_host = Proc.new { |source, request|
- if request.ssl?
- "#{request.protocol}#{request.host_with_port}"
- else
- "#{request.protocol}assets#{source.length}.example.com"
- end
- }
- config.perform_caching = true
-
- assert_equal '/javascripts/vanilla.js'.length, 23
- assert_dom_equal(
- %(<script src="http://assets23.example.com/javascripts/vanilla.js"></script>),
- javascript_include_tag(:all, :cache => 'vanilla')
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'vanilla.js'))
-
- class << @controller.request
- def protocol() 'https://' end
- def ssl?() true end
- end
-
- assert_equal '/javascripts/secure.js'.length, 22
- assert_dom_equal(
- %(<script src="https://localhost/javascripts/secure.js"></script>),
- javascript_include_tag(:all, :cache => 'secure')
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'secure.js'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'vanilla.js'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'secure.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_on_with_2_argument_object_asset_host
- ENV['RAILS_ASSET_ID'] = ''
- @controller.config.asset_host = Class.new do
- def call(source, request)
- if request.ssl?
- "#{request.protocol}#{request.host_with_port}"
- else
- "#{request.protocol}assets#{source.length}.example.com"
- end
- end
- end.new
-
- config.perform_caching = true
-
- assert_equal '/javascripts/vanilla.js'.length, 23
- assert_dom_equal(
- %(<script src="http://assets23.example.com/javascripts/vanilla.js"></script>),
- javascript_include_tag(:all, :cache => 'vanilla')
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'vanilla.js'))
-
- class << @controller.request
- def protocol() 'https://' end
- def ssl?() true end
- end
-
- assert_equal '/javascripts/secure.js'.length, 22
- assert_dom_equal(
- %(<script src="https://localhost/javascripts/secure.js"></script>),
- javascript_include_tag(:all, :cache => 'secure')
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'secure.js'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'vanilla.js'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'secure.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_on_and_using_subdirectory
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.asset_host = 'http://a%d.example.com'
- config.perform_caching = true
-
- number = Zlib.crc32('/javascripts/cache/money.js') % 4
- assert_dom_equal(
- %(<script src="http://a#{number}.example.com/javascripts/cache/money.js"></script>),
- javascript_include_tag(:all, :cache => "cache/money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'cache', 'money.js'))
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'cache', 'money.js'))
- end
-
- def test_caching_javascript_include_tag_with_all_and_recursive_puts_defaults_at_the_start_of_the_file
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.asset_host = 'http://a0.example.com'
- config.perform_caching = true
-
- assert_dom_equal(
- %(<script src="http://a0.example.com/javascripts/combined.js"></script>),
- javascript_include_tag(:all, :cache => "combined", :recursive => true)
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
-
- assert_equal(
- %(// prototype js\n\n// effects js\n\n// dragdrop js\n\n// controls js\n\n// bank js\n\n// robber js\n\n// subdir js\n\n\n// version.1.0 js\n\n// application js),
- IO.read(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
- )
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
- end
-
- def test_caching_javascript_include_tag_with_all_puts_defaults_at_the_start_of_the_file
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.asset_host = 'http://a0.example.com'
- config.perform_caching = true
-
- assert_dom_equal(
- %(<script src="http://a0.example.com/javascripts/combined.js"></script>),
- javascript_include_tag(:all, :cache => "combined")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
-
- assert_equal(
- %(// prototype js\n\n// effects js\n\n// dragdrop js\n\n// controls js\n\n// bank js\n\n// robber js\n\n// version.1.0 js\n\n// application js),
- IO.read(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
- )
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
- end
-
- def test_caching_javascript_include_tag_with_relative_url_root
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.relative_url_root = "/collaboration/hieraki"
- config.perform_caching = true
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/all.js"></script>),
- javascript_include_tag(:all, :cache => true)
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/money.js"></script>),
- javascript_include_tag(:all, :cache => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- # Same as above, but with script_name
- def test_caching_javascript_include_tag_with_script_name
- ENV["RAILS_ASSET_ID"] = ""
- @request.script_name = "/collaboration/hieraki"
- config.perform_caching = true
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/all.js"></script>),
- javascript_include_tag(:all, :cache => true)
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/money.js"></script>),
- javascript_include_tag(:all, :cache => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- def test_caching_javascript_include_tag_with_named_paths_and_relative_url_root_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.relative_url_root = "/collaboration/hieraki"
- config.perform_caching = false
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/robber.js"></script>),
- javascript_include_tag('robber', :cache => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/robber.js"></script>),
- javascript_include_tag('robber', :cache => "money", :recursive => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- # Same as above, but with script_name
- def test_caching_javascript_include_tag_with_named_paths_and_script_name_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
- @request.script_name = "/collaboration/hieraki"
- config.perform_caching = false
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/robber.js"></script>),
- javascript_include_tag('robber', :cache => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/robber.js"></script>),
- javascript_include_tag('robber', :cache => "money", :recursive => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = false
-
- assert_dom_equal(
- %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/version.1.0.js"></script>\n<script src="/javascripts/application.js"></script>),
- javascript_include_tag(:all, :cache => true)
- )
-
- assert_dom_equal(
- %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/subdir/subdir.js"></script>\n<script src="/javascripts/version.1.0.js"></script>\n<script src="/javascripts/application.js"></script>),
- javascript_include_tag(:all, :cache => true, :recursive => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_dom_equal(
- %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/version.1.0.js"></script>\n<script src="/javascripts/application.js"></script>),
- javascript_include_tag(:all, :cache => "money")
- )
-
- assert_dom_equal(
- %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/subdir/subdir.js"></script>\n<script src="/javascripts/version.1.0.js"></script>\n<script src="/javascripts/application.js"></script>),
- javascript_include_tag(:all, :cache => "money", :recursive => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_on_and_missing_javascript_file
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = true
-
- assert_raise(Errno::ENOENT) {
- javascript_include_tag('bank', 'robber', 'missing_security_guard', :cache => true)
- }
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_raise(Errno::ENOENT) {
- javascript_include_tag('bank', 'robber', 'missing_security_guard', :cache => "money")
- }
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_on_and_javascript_file_is_uri
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = true
-
- assert_raise(Errno::ENOENT) {
- javascript_include_tag('bank', 'robber', 'https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.js', :cache => true)
- }
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_off_and_missing_javascript_file
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = false
-
- assert_raise(Errno::ENOENT) {
- javascript_include_tag('bank', 'robber', 'missing_security_guard', :cache => true)
- }
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_raise(Errno::ENOENT) {
- javascript_include_tag('bank', 'robber', 'missing_security_guard', :cache => "money")
- }
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- def test_caching_stylesheet_link_tag_when_caching_on
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.asset_host = 'a0.example.com'
- config.perform_caching = true
-
- assert_dom_equal(
- %(<link href="http://a0.example.com/stylesheets/all.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => true)
- )
-
- files_to_be_joined = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/[^all]*.css"]
-
- expected_mtime = files_to_be_joined.map { |p| File.mtime(p) }.max
- assert_equal expected_mtime, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- bytes_added_by_join = "\n\n".size * files_to_be_joined.size - "\n\n".size
- expected_size = files_to_be_joined.sum { |p| File.size(p) } + bytes_added_by_join
- assert_equal expected_size, File.size(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="http://a0.example.com/stylesheets/money.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
-
- assert_dom_equal(
- %(<link href="http://a0.example.com/absolute/test.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => "/absolute/test")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute', 'test.css'))
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute'))
- end
-
- def test_concat_stylesheet_link_tag_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
-
- assert_dom_equal(
- %(<link href="/stylesheets/all.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :concat => true)
- )
-
- expected = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/*.css"].map { |p| File.mtime(p) }.max
- assert_equal expected, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="/stylesheets/money.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :concat => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
-
- assert_dom_equal(
- %(<link href="/absolute/test.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :concat => "/absolute/test")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute', 'test.css'))
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute'))
- end
-
- def test_caching_stylesheet_link_tag_when_caching_on_and_missing_css_file
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = true
-
- assert_raise(Errno::ENOENT) {
- stylesheet_link_tag('bank', 'robber', 'missing_security_guard', :cache => true)
- }
-
- assert ! File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_raise(Errno::ENOENT) {
- stylesheet_link_tag('bank', 'robber', 'missing_security_guard', :cache => "money")
- }
-
- assert ! File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
- def test_caching_stylesheet_link_tag_when_caching_off_and_missing_css_file
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = false
-
- assert_raise(Errno::ENOENT) {
- stylesheet_link_tag('bank', 'robber', 'missing_security_guard', :cache => true)
- }
-
- assert ! File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_raise(Errno::ENOENT) {
- stylesheet_link_tag('bank', 'robber', 'missing_security_guard', :cache => "money")
- }
-
- assert ! File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
- def test_caching_stylesheet_link_tag_when_caching_on_with_proc_asset_host
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.asset_host = Proc.new { |source| "a#{source.length}.example.com" }
- config.perform_caching = true
-
- assert_equal '/stylesheets/styles.css'.length, 23
- assert_dom_equal(
- %(<link href="http://a23.example.com/stylesheets/styles.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => 'styles')
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'styles.css'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'styles.css'))
- end
-
- def test_caching_stylesheet_link_tag_with_relative_url_root
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.relative_url_root = "/collaboration/hieraki"
- config.perform_caching = true
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/all.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => true)
- )
-
- files_to_be_joined = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/[^all]*.css"]
-
- expected_mtime = files_to_be_joined.map { |p| File.mtime(p) }.max
- assert_equal expected_mtime, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/money.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
- # Same as above, but with script_name
- def test_caching_stylesheet_link_tag_with_script_name
- ENV["RAILS_ASSET_ID"] = ""
- @request.script_name = "/collaboration/hieraki"
- config.perform_caching = true
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/all.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => true)
- )
-
- files_to_be_joined = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/[^all]*.css"]
-
- expected_mtime = files_to_be_joined.map { |p| File.mtime(p) }.max
- assert_equal expected_mtime, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/money.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
-
- def test_caching_stylesheet_link_tag_with_named_paths_and_relative_url_root_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.relative_url_root = "/collaboration/hieraki"
- config.perform_caching = false
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag('robber', :cache => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag('robber', :cache => "money")
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
- # Same as above, but with script_name
- def test_caching_stylesheet_link_tag_with_named_paths_and_script_name_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
- @request.script_name = "/collaboration/hieraki"
- config.perform_caching = false
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag('robber', :cache => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag('robber', :cache => "money")
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
- def test_caching_stylesheet_include_tag_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = false
-
- assert_dom_equal(
- %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => true)
- )
-
- assert_dom_equal(
- %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => true, :recursive => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => "money")
- )
-
- assert_dom_equal(
- %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => "money", :recursive => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
- def test_caching_stylesheet_include_tag_with_absolute_uri
- ENV["RAILS_ASSET_ID"] = ""
-
- assert_dom_equal(
- %(<link href="/stylesheets/all.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag("/foo/baz", :cache => true)
- )
-
- FileUtils.rm(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- end
end
class AssetTagHelperNonVhostTest < ActionView::TestCase
tests ActionView::Helpers::AssetTagHelper
+ attr_reader :request
+
def setup
super
@controller = BasicController.new
@controller.config.relative_url_root = "/collaboration/hieraki"
- @request = Struct.new(:protocol).new("gopher://")
+ @request = Struct.new(:protocol, :base_url).new("gopher://", "gopher://www.example.com")
@controller.request = @request
-
- JavascriptIncludeTag.expansions.clear
end
def url_for(options)
@@ -1351,10 +549,33 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(/collaboration/hieraki/images/xml.png), image_path("xml.png"))
end
+ def test_should_return_nothing_if_asset_host_isnt_configured
+ assert_equal nil, compute_asset_host("foo")
+ end
+
+ def test_should_current_request_host_is_always_returned_for_request
+ assert_equal "gopher://www.example.com", compute_asset_host("foo", :protocol => :request)
+ end
+
def test_should_ignore_relative_root_path_on_complete_url
assert_dom_equal(%(http://www.example.com/images/xml.png), image_path("http://www.example.com/images/xml.png"))
end
+ def test_should_return_simple_string_asset_host
+ @controller.config.asset_host = "assets.example.com"
+ assert_equal "gopher://assets.example.com", compute_asset_host("foo")
+ end
+
+ def test_should_return_relative_asset_host
+ @controller.config.asset_host = "assets.example.com"
+ assert_equal "//assets.example.com", compute_asset_host("foo", :protocol => :relative)
+ end
+
+ def test_should_return_custom_protocol_asset_host
+ @controller.config.asset_host = "assets.example.com"
+ assert_equal "ftp://assets.example.com", compute_asset_host("foo", :protocol => "ftp")
+ end
+
def test_should_compute_proper_path_with_asset_host
@controller.config.asset_host = "assets.example.com"
assert_dom_equal(%(<link href="http://www.example.com/collaboration/hieraki" rel="alternate" title="RSS" type="application/rss+xml" />), auto_discovery_link_tag)
@@ -1387,6 +608,11 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/images/xml.png), image_url("xml.png"))
end
+ def test_should_return_asset_host_with_protocol
+ @controller.config.asset_host = "http://assets.example.com"
+ assert_equal "http://assets.example.com", compute_asset_host("foo")
+ end
+
def test_should_ignore_asset_host_on_complete_url
@controller.config.asset_host = "http://assets.example.com"
assert_dom_equal(%(<link href="http://bar.example.com/stylesheets/style.css" media="screen" rel="stylesheet" />), stylesheet_link_tag("http://bar.example.com/stylesheets/style.css"))
@@ -1397,6 +623,11 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(<link href="//bar.example.com/stylesheets/style.css" media="screen" rel="stylesheet" />), stylesheet_link_tag("//bar.example.com/stylesheets/style.css"))
end
+ def test_should_wildcard_asset_host
+ @controller.config.asset_host = 'http://a%d.example.com'
+ assert_match(%r(http://a[0123].example.com), compute_asset_host("foo"))
+ end
+
def test_should_wildcard_asset_host_between_zero_and_four
@controller.config.asset_host = 'http://a%d.example.com'
assert_match(%r(http://a[0123].example.com/collaboration/hieraki/images/xml.png), image_path('xml.png'))
@@ -1420,3 +651,73 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
assert_dom_equal(%(/collaboration/hieraki/stylesheets/foo.css), stylesheet_path("foo"))
end
end
+
+class AssetUrlHelperControllerTest < ActionView::TestCase
+ tests ActionView::Helpers::AssetUrlHelper
+
+ def setup
+ super
+
+ @controller = BasicController.new
+ @controller.extend ActionView::Helpers::AssetUrlHelper
+
+ @request = Class.new do
+ attr_accessor :script_name
+ def protocol() 'http://' end
+ def ssl?() false end
+ def host_with_port() 'www.example.com' end
+ def base_url() 'http://www.example.com' end
+ end.new
+
+ @controller.request = @request
+ end
+
+ def test_asset_path
+ assert_equal "/foo", @controller.asset_path("foo")
+ end
+
+ def test_asset_url
+ assert_equal "http://www.example.com/foo", @controller.asset_url("foo")
+ end
+end
+
+class AssetUrlHelperEmptyModuleTest < ActionView::TestCase
+ tests ActionView::Helpers::AssetUrlHelper
+
+ def setup
+ super
+
+ @module = Module.new
+ @module.extend ActionView::Helpers::AssetUrlHelper
+ end
+
+ def test_asset_path
+ assert_equal "/foo", @module.asset_path("foo")
+ end
+
+ def test_asset_url
+ assert_equal "/foo", @module.asset_url("foo")
+ end
+
+ def test_asset_url_with_request
+ @module.instance_eval do
+ def request
+ Struct.new(:base_url, :script_name).new("http://www.example.com", nil)
+ end
+ end
+
+ assert @module.request
+ assert_equal "http://www.example.com/foo", @module.asset_url("foo")
+ end
+
+ def test_asset_url_with_config_asset_host
+ @module.instance_eval do
+ def config
+ Struct.new(:asset_host).new("http://www.example.com")
+ end
+ end
+
+ assert @module.config.asset_host
+ assert_equal "http://www.example.com/foo", @module.asset_url("foo")
+ end
+end
diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb
index 63066d40cd..495a9d3f9d 100644
--- a/actionpack/test/template/date_helper_i18n_test.rb
+++ b/actionpack/test/template/date_helper_i18n_test.rb
@@ -36,16 +36,13 @@ class DateHelperDistanceOfTimeInWordsI18nTests < ActiveSupport::TestCase
end
end
- def assert_distance_of_time_in_words_translates_key(passed, expected)
- diff, passed_options = *passed
- key, count = *expected
- to = @from + diff
-
- options = {:locale => 'en', :scope => :'datetime.distance_in_words'}
- options[:count] = count if count
-
- I18n.expects(:t).with(key, options)
- distance_of_time_in_words(@from, to, passed_options.merge(:locale => 'en'))
+ def test_distance_of_time_in_words_calls_i18n_with_custom_scope
+ {
+ [30.days, { scope: :'datetime.distance_in_words_ago' }] => [:'about_x_months', 1],
+ [60.days, { scope: :'datetime.distance_in_words_ago' }] => [:'x_months', 2],
+ }.each do |passed, expected|
+ assert_distance_of_time_in_words_translates_key(passed, expected, scope: :'datetime.distance_in_words_ago')
+ end
end
def test_time_ago_in_words_passes_locale
@@ -74,6 +71,18 @@ class DateHelperDistanceOfTimeInWordsI18nTests < ActiveSupport::TestCase
assert_equal expected, I18n.t(key, :count => count, :scope => 'datetime.distance_in_words')
end
end
+
+ def assert_distance_of_time_in_words_translates_key(passed, expected, expected_options = {})
+ diff, passed_options = *passed
+ key, count = *expected
+ to = @from + diff
+
+ options = { locale: 'en', scope: :'datetime.distance_in_words' }.merge!(expected_options)
+ options[:count] = count if count
+
+ I18n.expects(:t).with(key, options)
+ distance_of_time_in_words(@from, to, passed_options.merge(locale: 'en'))
+ end
end
class DateHelperSelectTagsI18nTests < ActiveSupport::TestCase
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index a4da7cd4b0..8bd8eff3c0 100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -1007,6 +1007,22 @@ class DateHelperTest < ActionView::TestCase
assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { :date_separator => " / ", :prefix => "date[first]", :use_hidden => true })
end
+ def test_select_date_with_css_classes_option
+ expected = %(<select id="date_first_year" name="date[first][year]" class="year">\n)
+ expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_first_month" name="date[first][month]" class="month">\n)
+ expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n)
+ expected << "</select>\n"
+
+ expected << %(<select id="date_first_day" name="date[first][day]" class="day">\n)
+ expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n)
+ expected << "</select>\n"
+
+ assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), {:start_year => 2003, :end_year => 2005, :prefix => "date[first]", :with_css_classes => true})
+ end
+
def test_select_datetime
expected = %(<select id="date_first_year" name="date[first][year]">\n)
expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n)
diff --git a/actionpack/test/template/digestor_test.rb b/actionpack/test/template/digestor_test.rb
index 01b101cb49..b9d26da3af 100644
--- a/actionpack/test/template/digestor_test.rb
+++ b/actionpack/test/template/digestor_test.rb
@@ -13,20 +13,24 @@ end
class FixtureFinder
FIXTURES_DIR = "#{File.dirname(__FILE__)}/../fixtures/digestor"
- TMP_DIR = "#{File.dirname(__FILE__)}/../tmp"
def find(logical_name, keys, partial, options)
- FixtureTemplate.new("#{TMP_DIR}/digestor/#{partial ? logical_name.gsub(%r|/([^/]+)$|, '/_\1') : logical_name}.#{options[:formats].first}.erb")
+ FixtureTemplate.new("digestor/#{partial ? logical_name.gsub(%r|/([^/]+)$|, '/_\1') : logical_name}.#{options[:formats].first}.erb")
end
end
class TemplateDigestorTest < ActionView::TestCase
def setup
- FileUtils.cp_r FixtureFinder::FIXTURES_DIR, FixtureFinder::TMP_DIR
+ @cwd = Dir.pwd
+ @tmp_dir = Dir.mktmpdir
+
+ FileUtils.cp_r FixtureFinder::FIXTURES_DIR, @tmp_dir
+ Dir.chdir @tmp_dir
end
def teardown
- FileUtils.rm_r File.join(FixtureFinder::TMP_DIR, "digestor")
+ Dir.chdir @cwd
+ FileUtils.rm_r @tmp_dir
ActionView::Digestor.cache.clear
end
@@ -59,6 +63,12 @@ class TemplateDigestorTest < ActionView::TestCase
change_template("comments/_comment")
end
end
+
+ def test_directory_depth_dependency
+ assert_digest_difference("level/below/index") do
+ change_template("level/below/_header")
+ end
+ end
def test_logging_of_missing_template
assert_logged "Couldn't find template for digesting: messages/something_missing.html" do
@@ -153,7 +163,7 @@ class TemplateDigestorTest < ActionView::TestCase
end
def change_template(template_name)
- File.open("#{FixtureFinder::TMP_DIR}/digestor/#{template_name}.html.erb", "w") do |f|
+ File.open("digestor/#{template_name}.html.erb", "w") do |f|
f.write "\nTHIS WAS CHANGED!"
end
end
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 246c4bfada..fbfc73deda 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -400,6 +400,12 @@ class FormHelperTest < ActionView::TestCase
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
check_box("post", "secret")
)
+
+ @post.secret = Set.new(['1'])
+ assert_dom_equal(
+ '<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
+ check_box("post", "secret")
+ )
end
def test_check_box_with_include_hidden_false
diff --git a/actionpack/test/template/html-scanner/sanitizer_test.rb b/actionpack/test/template/html-scanner/sanitizer_test.rb
index 324caef224..d9b57776c9 100644
--- a/actionpack/test/template/html-scanner/sanitizer_test.rb
+++ b/actionpack/test/template/html-scanner/sanitizer_test.rb
@@ -233,7 +233,7 @@ class SanitizerTest < ActionController::TestCase
end
def test_should_sanitize_attributes
- assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="'&gt;&lt;script&gt;alert()&lt;/script&gt;">blah</span>)
+ assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="#{CGI.escapeHTML "'><script>alert()</script>"}">blah</span>)
end
def test_should_sanitize_illegal_style_properties
diff --git a/actionpack/test/template/sanitize_helper_test.rb b/actionpack/test/template/sanitize_helper_test.rb
index 7626cdf386..12d5260a9d 100644
--- a/actionpack/test/template/sanitize_helper_test.rb
+++ b/actionpack/test/template/sanitize_helper_test.rb
@@ -17,7 +17,7 @@ class SanitizeHelperTest < ActionView::TestCase
end
def test_sanitize_form
- assert_sanitized "<form action=\"/foo/bar\" method=\"post\"><input></form>", ''
+ assert_equal '', sanitize("<form action=\"/foo/bar\" method=\"post\"><input></form>")
end
def test_should_sanitize_illegal_style_properties
@@ -48,8 +48,4 @@ class SanitizeHelperTest < ActionView::TestCase
def test_sanitize_is_marked_safe
assert sanitize("<html><script></script></html>").html_safe?
end
-
- def assert_sanitized(text, expected = nil)
- assert_equal((expected || text), sanitize(text))
- end
end
diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb
index 5265ae6b3a..c7231d9cd5 100644
--- a/actionpack/test/template/test_case_test.rb
+++ b/actionpack/test/template/test_case_test.rb
@@ -321,6 +321,14 @@ module ActionView
assert_template :partial => "_partial_for_use_in_layout", :locals => { :name => "Somebody Else" }
end
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
+
end
module AHelperWithInitialize
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 134177d74d..bbfdf7f944 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -31,17 +31,17 @@ class UrlHelperTest < ActiveSupport::TestCase
setup :_prepare_context
def hash_for(options = {})
- { :controller => "foo", :action => "bar" }.merge!(options)
+ { controller: "foo", action: "bar" }.merge!(options)
end
alias url_hash hash_for
def test_url_for_does_not_escape_urls
- assert_equal "/?a=b&c=d", url_for(hash_for(:a => :b, :c => :d))
+ assert_equal "/?a=b&c=d", url_for(hash_for(a: :b, c: :d))
end
def test_url_for_with_back
referer = 'http://www.example.com/referer'
- @controller = Struct.new(:request).new(Struct.new(:env).new({"HTTP_REFERER" => referer}))
+ @controller = Struct.new(:request).new(Struct.new(:env).new("HTTP_REFERER" => referer))
assert_equal 'http://www.example.com/referer', url_for(:back)
end
@@ -53,7 +53,7 @@ class UrlHelperTest < ActiveSupport::TestCase
# TODO: missing test cases
def test_button_to_with_straight_url
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com")
+ assert_dom_equal %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com")
end
def test_button_to_with_straight_url_and_request_forgery
@@ -68,143 +68,146 @@ class UrlHelperTest < ActiveSupport::TestCase
end
def test_button_to_with_form_class
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"custom-class\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com", :form_class => 'custom-class')
+ assert_dom_equal %{<form method="post" action="http://www.example.com" class="custom-class"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com", form_class: 'custom-class')
end
def test_button_to_with_form_class_escapes
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"&lt;script&gt;evil_js&lt;/script&gt;\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com", :form_class => '<script>evil_js</script>')
+ assert_dom_equal %{<form method="post" action="http://www.example.com" class="&lt;script&gt;evil_js&lt;/script&gt;"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com", form_class: '<script>evil_js</script>')
end
def test_button_to_with_query
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&q2=v2")
+ assert_dom_equal %{<form method="post" action="http://www.example.com/q1=v1&amp;q2=v2" class="button_to"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com/q1=v1&q2=v2")
end
def test_button_to_with_html_safe_URL
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&amp;q2=v2".html_safe)
+ assert_dom_equal %{<form method="post" action="http://www.example.com/q1=v1&amp;q2=v2" class="button_to"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com/q1=v1&amp;q2=v2".html_safe)
end
def test_button_to_with_query_and_no_name
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com?q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"http://www.example.com?q1=v1&amp;q2=v2\" /></div></form>", button_to(nil, "http://www.example.com?q1=v1&q2=v2")
+ assert_dom_equal %{<form method="post" action="http://www.example.com?q1=v1&amp;q2=v2" class="button_to"><div><input type="submit" value="http://www.example.com?q1=v1&amp;q2=v2" /></div></form>}, button_to(nil, "http://www.example.com?q1=v1&q2=v2")
end
def test_button_to_with_javascript_confirm
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :data => { :confirm => "Are you sure?" })
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input data-confirm="Are you sure?" type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", data: { confirm: "Are you sure?" })
)
end
def test_button_to_with_deprecated_confirm
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input data-confirm="Are you sure?" type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", confirm: "Are you sure?")
)
end
end
def test_button_to_with_javascript_disable_with
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :data => { :disable_with => "Greeting..." })
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input data-disable-with="Greeting..." type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", data: { disable_with: "Greeting..." })
)
end
def test_button_to_with_javascript_deprecated_disable_with
assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead" do
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :disable_with => "Greeting...")
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input data-disable-with="Greeting..." type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", disable_with: "Greeting...")
)
end
end
def test_button_to_with_remote_and_form_options
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"custom-class\" data-remote=\"true\" data-type=\"json\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com", :remote => true, :form => { :class => "custom-class", "data-type" => "json" } )
+ assert_dom_equal(
+ %{<form method="post" action="http://www.example.com" class="custom-class" data-remote="true" data-type="json"><div><input type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", remote: true, form: { class: "custom-class", "data-type" => "json" })
+ )
end
def test_button_to_with_remote_and_javascript_confirm
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => true, :data => { :confirm => "Are you sure?" })
+ %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><div><input data-confirm="Are you sure?" type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", remote: true, data: { confirm: "Are you sure?" })
)
end
def test_button_to_with_remote_and_javascript_with_deprecated_confirm
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => true, :confirm => "Are you sure?")
+ %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><div><input data-confirm="Are you sure?" type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", remote: true, confirm: "Are you sure?")
)
end
end
def test_button_to_with_remote_and_javascript_disable_with
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => true, :data => { :disable_with => "Greeting..." })
+ %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><div><input data-disable-with="Greeting..." type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", remote: true, data: { disable_with: "Greeting..." })
)
end
def test_button_to_with_remote_and_javascript_deprecated_disable_with
assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead" do
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => true, :disable_with => "Greeting...")
+ %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><div><input data-disable-with="Greeting..." type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", remote: true, disable_with: "Greeting...")
)
end
end
def test_button_to_with_remote_false
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => false)
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", remote: false)
)
end
def test_button_to_enabled_disabled
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :disabled => false)
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", disabled: false)
)
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input disabled=\"disabled\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :disabled => true)
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input disabled="disabled" type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", disabled: true)
)
end
def test_button_to_with_method_delete
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"hidden\" name=\"_method\" value=\"delete\" /><input type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :method => :delete)
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="hidden" name="_method" value="delete" /><input type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", method: :delete)
)
end
def test_button_to_with_method_get
assert_dom_equal(
- "<form method=\"get\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :method => :get)
+ %{<form method="get" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", method: :get)
)
end
def test_button_to_with_block
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><button type=\"submit\"><span>Hello</span></button></div></form>",
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><button type="submit"><span>Hello</span></button></div></form>},
button_to("http://www.example.com") { content_tag(:span, 'Hello') }
)
end
def test_link_tag_with_straight_url
- assert_dom_equal "<a href=\"http://www.example.com\">Hello</a>", link_to("Hello", "http://www.example.com")
+ assert_dom_equal %{<a href="http://www.example.com">Hello</a>}, link_to("Hello", "http://www.example.com")
end
def test_link_tag_without_host_option
- assert_dom_equal(%q{<a href="/">Test Link</a>}, link_to('Test Link', url_hash))
+ assert_dom_equal(%{<a href="/">Test Link</a>}, link_to('Test Link', url_hash))
end
def test_link_tag_with_host_option
- hash = hash_for(:host => "www.example.com")
- expected = %q{<a href="http://www.example.com/">Test Link</a>}
+ hash = hash_for(host: "www.example.com")
+ expected = %{<a href="http://www.example.com/">Test Link</a>}
assert_dom_equal(expected, link_to('Test Link', hash))
end
@@ -243,131 +246,143 @@ class UrlHelperTest < ActiveSupport::TestCase
end
def test_link_tag_with_custom_onclick
- link = link_to("Hello", "http://www.example.com", :onclick => "alert('yay!')")
+ link = link_to("Hello", "http://www.example.com", onclick: "alert('yay!')")
expected = %{<a href="http://www.example.com" onclick="alert(&#39;yay!&#39;)">Hello</a>}
assert_dom_equal expected, link
end
def test_link_tag_with_javascript_confirm
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"Are you sure?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :data => { :confirm => "Are you sure?" })
+ %{<a href="http://www.example.com" data-confirm="Are you sure?">Hello</a>},
+ link_to("Hello", "http://www.example.com", data: { confirm: "Are you sure?" })
)
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"You cant possibly be sure, can you?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :data => { :confirm => "You cant possibly be sure, can you?" })
+ %{<a href="http://www.example.com" data-confirm="You cant possibly be sure, can you?">Hello</a>},
+ link_to("Hello", "http://www.example.com", data: { confirm: "You cant possibly be sure, can you?" })
)
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"You cant possibly be sure,\n can you?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :data => { :confirm => "You cant possibly be sure,\n can you?" })
+ %{<a href="http://www.example.com" data-confirm="You cant possibly be sure,\n can you?">Hello</a>},
+ link_to("Hello", "http://www.example.com", data: { confirm: "You cant possibly be sure,\n can you?" })
)
end
def test_link_tag_with_deprecated_confirm
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"Are you sure?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
+ %{<a href="http://www.example.com" data-confirm="Are you sure?">Hello</a>},
+ link_to("Hello", "http://www.example.com", confirm: "Are you sure?")
)
end
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"You cant possibly be sure, can you?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :confirm => "You cant possibly be sure, can you?")
+ %{<a href="http://www.example.com" data-confirm="You cant possibly be sure, can you?">Hello</a>},
+ link_to("Hello", "http://www.example.com", confirm: "You cant possibly be sure, can you?")
)
end
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"You cant possibly be sure,\n can you?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :confirm => "You cant possibly be sure,\n can you?")
+ %{<a href="http://www.example.com" data-confirm="You cant possibly be sure,\n can you?">Hello</a>},
+ link_to("Hello", "http://www.example.com", confirm: "You cant possibly be sure,\n can you?")
)
end
end
def test_link_to_with_remote
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-remote=\"true\">Hello</a>",
- link_to("Hello", "http://www.example.com", :remote => true)
+ %{<a href="http://www.example.com" data-remote="true">Hello</a>},
+ link_to("Hello", "http://www.example.com", remote: true)
)
end
def test_link_to_with_remote_false
assert_dom_equal(
- "<a href=\"http://www.example.com\">Hello</a>",
- link_to("Hello", "http://www.example.com", :remote => false)
+ %{<a href="http://www.example.com">Hello</a>},
+ link_to("Hello", "http://www.example.com", remote: false)
+ )
+ end
+
+ def test_link_to_with_symbolic_remote_in_non_html_options
+ assert_dom_equal(
+ %{<a href="/" data-remote="true">Hello</a>},
+ link_to("Hello", hash_for(remote: true), {})
+ )
+ end
+
+ def test_link_to_with_string_remote_in_non_html_options
+ assert_dom_equal(
+ %{<a href="/" data-remote="true">Hello</a>},
+ link_to("Hello", hash_for('remote' => true), {})
)
end
def test_link_tag_using_post_javascript
assert_dom_equal(
- "<a href='http://www.example.com' data-method=\"post\" rel=\"nofollow\">Hello</a>",
- link_to("Hello", "http://www.example.com", :method => :post)
+ %{<a href="http://www.example.com" data-method="post" rel="nofollow">Hello</a>},
+ link_to("Hello", "http://www.example.com", method: :post)
)
end
def test_link_tag_using_delete_javascript
assert_dom_equal(
- "<a href='http://www.example.com' rel=\"nofollow\" data-method=\"delete\">Destroy</a>",
- link_to("Destroy", "http://www.example.com", :method => :delete)
+ %{<a href="http://www.example.com" rel="nofollow" data-method="delete">Destroy</a>},
+ link_to("Destroy", "http://www.example.com", method: :delete)
)
end
def test_link_tag_using_delete_javascript_and_href
assert_dom_equal(
- "<a href='\#' rel=\"nofollow\" data-method=\"delete\">Destroy</a>",
- link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#')
+ %{<a href="\#" rel="nofollow" data-method="delete">Destroy</a>},
+ link_to("Destroy", "http://www.example.com", method: :delete, href: '#')
)
end
def test_link_tag_using_post_javascript_and_rel
assert_dom_equal(
- "<a href='http://www.example.com' data-method=\"post\" rel=\"example nofollow\">Hello</a>",
- link_to("Hello", "http://www.example.com", :method => :post, :rel => 'example')
+ %{<a href="http://www.example.com" data-method="post" rel="example nofollow">Hello</a>},
+ link_to("Hello", "http://www.example.com", method: :post, rel: 'example')
)
end
def test_link_tag_using_post_javascript_and_confirm
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-method=\"post\" rel=\"nofollow\" data-confirm=\"Are you serious?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :method => :post, :data => { :confirm => "Are you serious?" })
+ %{<a href="http://www.example.com" data-method="post" rel="nofollow" data-confirm="Are you serious?">Hello</a>},
+ link_to("Hello", "http://www.example.com", method: :post, data: { confirm: "Are you serious?" })
)
end
def test_link_tag_using_post_javascript_and_with_deprecated_confirm
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-method=\"post\" rel=\"nofollow\" data-confirm=\"Are you serious?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :method => :post, :confirm => "Are you serious?")
+ %{<a href="http://www.example.com" data-method="post" rel="nofollow" data-confirm="Are you serious?">Hello</a>},
+ link_to("Hello", "http://www.example.com", method: :post, confirm: "Are you serious?")
)
end
end
def test_link_tag_using_delete_javascript_and_href_and_confirm
assert_dom_equal(
- "<a href='\#' rel=\"nofollow\" data-confirm=\"Are you serious?\" data-method=\"delete\">Destroy</a>",
- link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :data => { :confirm => "Are you serious?" }),
- "When specifying url, form should be generated with it, but not this.href"
+ %{<a href="\#" rel="nofollow" data-confirm="Are you serious?" data-method="delete">Destroy</a>},
+ link_to("Destroy", "http://www.example.com", method: :delete, href: '#', data: { confirm: "Are you serious?" })
)
end
def test_link_tag_using_delete_javascript_and_href_and_with_deprecated_confirm
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<a href='\#' rel=\"nofollow\" data-confirm=\"Are you serious?\" data-method=\"delete\">Destroy</a>",
- link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :confirm => "Are you serious?"),
- "When specifying url, form should be generated with it, but not this.href"
+ %{<a href="\#" rel="nofollow" data-confirm="Are you serious?" data-method="delete">Destroy</a>},
+ link_to("Destroy", "http://www.example.com", method: :delete, href: '#', confirm: "Are you serious?")
)
end
end
def test_link_tag_with_block
- assert_dom_equal '<a href="/"><span>Example site</span></a>',
+ assert_dom_equal %{<a href="/"><span>Example site</span></a>},
link_to('/') { content_tag(:span, 'Example site') }
end
def test_link_tag_with_block_and_html_options
- assert_dom_equal '<a class="special" href="/"><span>Example site</span></a>',
- link_to('/', :class => "special") { content_tag(:span, 'Example site') }
+ assert_dom_equal %{<a class="special" href="/"><span>Example site</span></a>},
+ link_to('/', class: "special") { content_tag(:span, 'Example site') }
end
def test_link_tag_using_block_in_erb
@@ -377,18 +392,18 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_link_tag_with_html_safe_string
assert_dom_equal(
- "<a href=\"/article/Gerd_M%C3%BCller\">Gerd Müller</a>",
+ %{<a href="/article/Gerd_M%C3%BCller">Gerd Müller</a>},
link_to("Gerd Müller", article_path("Gerd_Müller".html_safe))
)
end
def test_link_tag_escapes_content
- assert_dom_equal '<a href="/">Malicious &lt;script&gt;content&lt;/script&gt;</a>',
+ assert_dom_equal %{<a href="/">Malicious &lt;script&gt;content&lt;/script&gt;</a>},
link_to("Malicious <script>content</script>", "/")
end
def test_link_tag_does_not_escape_html_safe_content
- assert_dom_equal '<a href="/">Malicious <script>content</script></a>',
+ assert_dom_equal %{<a href="/">Malicious <script>content</script></a>},
link_to("Malicious <script>content</script>".html_safe, "/")
end
@@ -438,12 +453,12 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_current_page_with_params_that_match
@request = request_for_url("/?order=desc&page=1")
- assert current_page?(hash_for(:order => "desc", :page => "1"))
+ assert current_page?(hash_for(order: "desc", page: "1"))
assert current_page?("http://www.example.com/?order=desc&page=1")
end
def test_current_page_with_not_get_verb
- @request = request_for_url("/events", :method => :post)
+ @request = request_for_url("/events", method: :post)
assert !current_page?('/events')
end
@@ -466,20 +481,20 @@ class UrlHelperTest < ActiveSupport::TestCase
@request = request_for_url("/?order=desc&page=1")
assert_equal "Showing",
- link_to_unless_current("Showing", hash_for(:order => 'desc', :page => '1'))
+ link_to_unless_current("Showing", hash_for(order: 'desc', page: '1'))
assert_equal "Showing",
link_to_unless_current("Showing", "http://www.example.com/?order=desc&page=1")
@request = request_for_url("/?order=desc")
assert_equal %{<a href="/?order=asc">Showing</a>},
- link_to_unless_current("Showing", hash_for(:order => :asc))
+ link_to_unless_current("Showing", hash_for(order: :asc))
assert_equal %{<a href="http://www.example.com/?order=asc">Showing</a>},
link_to_unless_current("Showing", "http://www.example.com/?order=asc")
@request = request_for_url("/?order=desc")
assert_equal %{<a href="/?order=desc&amp;page=2\">Showing</a>},
- link_to_unless_current("Showing", hash_for(:order => "desc", :page => 2))
+ link_to_unless_current("Showing", hash_for(order: "desc", page: 2))
assert_equal %{<a href="http://www.example.com/?order=desc&amp;page=2">Showing</a>},
link_to_unless_current("Showing", "http://www.example.com/?order=desc&page=2")
@@ -492,56 +507,86 @@ class UrlHelperTest < ActiveSupport::TestCase
end
def test_mail_to
- assert_dom_equal "<a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>", mail_to("david@loudthinking.com")
- assert_dom_equal "<a href=\"mailto:david@loudthinking.com\">David Heinemeier Hansson</a>", mail_to("david@loudthinking.com", "David Heinemeier Hansson")
+ assert_dom_equal %{<a href="mailto:david@loudthinking.com">david@loudthinking.com</a>}, mail_to("david@loudthinking.com")
+ assert_dom_equal %{<a href="mailto:david@loudthinking.com">David Heinemeier Hansson</a>}, mail_to("david@loudthinking.com", "David Heinemeier Hansson")
assert_dom_equal(
- "<a class=\"admin\" href=\"mailto:david@loudthinking.com\">David Heinemeier Hansson</a>",
+ %{<a class="admin" href="mailto:david@loudthinking.com">David Heinemeier Hansson</a>},
mail_to("david@loudthinking.com", "David Heinemeier Hansson", "class" => "admin")
)
assert_equal mail_to("david@loudthinking.com", "David Heinemeier Hansson", "class" => "admin"),
- mail_to("david@loudthinking.com", "David Heinemeier Hansson", :class => "admin")
+ mail_to("david@loudthinking.com", "David Heinemeier Hansson", class: "admin")
end
def test_mail_to_with_javascript
- snippet = mail_to("me@domain.com", "My email", :encode => "javascript")
+ snippet = mail_to("me@domain.com", "My email", encode: "javascript")
assert_dom_equal "<script>eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%4d%79%20%65%6d%61%69%6c%3c%5c%2f%61%3e%27%29%3b'))</script>", snippet
end
def test_mail_to_with_javascript_unicode
- snippet = mail_to("unicode@example.com", "únicode", :encode => "javascript")
+ snippet = mail_to("unicode@example.com", "únicode", encode: "javascript")
assert_dom_equal "<script>eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%75%6e%69%63%6f%64%65%40%65%78%61%6d%70%6c%65%2e%63%6f%6d%5c%22%3e%c3%ba%6e%69%63%6f%64%65%3c%5c%2f%61%3e%27%29%3b'))</script>", snippet
end
def test_mail_with_options
assert_dom_equal(
- %(<a href="mailto:me@example.com?cc=ccaddress%40example.com&amp;bcc=bccaddress%40example.com&amp;body=This%20is%20the%20body%20of%20the%20message.&amp;subject=This%20is%20an%20example%20email">My email</a>),
- mail_to("me@example.com", "My email", :cc => "ccaddress@example.com", :bcc => "bccaddress@example.com", :subject => "This is an example email", :body => "This is the body of the message.")
+ %{<a href="mailto:me@example.com?cc=ccaddress%40example.com&amp;bcc=bccaddress%40example.com&amp;body=This%20is%20the%20body%20of%20the%20message.&amp;subject=This%20is%20an%20example%20email">My email</a>},
+ mail_to("me@example.com", "My email", cc: "ccaddress@example.com", bcc: "bccaddress@example.com", subject: "This is an example email", body: "This is the body of the message.")
)
end
def test_mail_to_with_img
- assert_dom_equal %(<a href="mailto:feedback@example.com"><img src="/feedback.png" /></a>),
+ assert_dom_equal %{<a href="mailto:feedback@example.com"><img src="/feedback.png" /></a>},
mail_to('feedback@example.com', '<img src="/feedback.png" />'.html_safe)
end
def test_mail_to_with_hex
- assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me@domain.com", "My email", :encode => "hex")
- assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#64;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>", mail_to("me@domain.com", nil, :encode => "hex")
+ assert_dom_equal(
+ %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>},
+ mail_to("me@domain.com", "My email", encode: "hex")
+ )
+
+ assert_dom_equal(
+ %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">&#109;&#101;&#64;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>},
+ mail_to("me@domain.com", nil, encode: "hex")
+ )
end
def test_mail_to_with_replace_options
- assert_dom_equal "<a href=\"mailto:wolfgang@stufenlos.net\">wolfgang(at)stufenlos(dot)net</a>", mail_to("wolfgang@stufenlos.net", nil, :replace_at => "(at)", :replace_dot => "(dot)")
- assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)")
- assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me@domain.com", "My email", :encode => "hex", :replace_at => "(at)")
- assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#40;&#100;&#111;&#116;&#41;&#99;&#111;&#109;</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)")
- assert_dom_equal "<script>eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%4d%79%20%65%6d%61%69%6c%3c%5c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
- assert_dom_equal "<script>eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%5c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
+ assert_dom_equal(
+ %{<a href="mailto:wolfgang@stufenlos.net">wolfgang(at)stufenlos(dot)net</a>},
+ mail_to("wolfgang@stufenlos.net", nil, replace_at: "(at)", replace_dot: "(dot)")
+ )
+
+ assert_dom_equal(
+ %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>},
+ mail_to("me@domain.com", nil, encode: "hex", replace_at: "(at)")
+ )
+
+ assert_dom_equal(
+ %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>},
+ mail_to("me@domain.com", "My email", encode: "hex", replace_at: "(at)")
+ )
+
+ assert_dom_equal(
+ %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#40;&#100;&#111;&#116;&#41;&#99;&#111;&#109;</a>},
+ mail_to("me@domain.com", nil, encode: "hex", replace_at: "(at)", replace_dot: "(dot)")
+ )
+
+ assert_dom_equal(
+ %{<script>eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%4d%79%20%65%6d%61%69%6c%3c%5c%2f%61%3e%27%29%3b'))</script>},
+ mail_to("me@domain.com", "My email", encode: "javascript", replace_at: "(at)", replace_dot: "(dot)")
+ )
+
+ assert_dom_equal(
+ %{<script>eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%5c%2f%61%3e%27%29%3b'))</script>},
+ mail_to("me@domain.com", nil, encode: "javascript", replace_at: "(at)", replace_dot: "(dot)")
+ )
end
def test_mail_to_returns_html_safe_string
assert mail_to("david@loudthinking.com").html_safe?
- assert mail_to("me@domain.com", "My email", :encode => "javascript").html_safe?
- assert mail_to("me@domain.com", "My email", :encode => "hex").html_safe?
+ assert mail_to("me@domain.com", "My email", encode: "javascript").html_safe?
+ assert mail_to("me@domain.com", "My email", encode: "hex").html_safe?
end
def protect_against_forgery?
@@ -568,62 +613,62 @@ class UrlHelperControllerTest < ActionController::TestCase
class UrlHelperController < ActionController::Base
test_routes do
get 'url_helper_controller_test/url_helper/show/:id',
- :to => 'url_helper_controller_test/url_helper#show',
- :as => :show
+ to: 'url_helper_controller_test/url_helper#show',
+ as: :show
get 'url_helper_controller_test/url_helper/profile/:name',
- :to => 'url_helper_controller_test/url_helper#show',
- :as => :profile
+ to: 'url_helper_controller_test/url_helper#show',
+ as: :profile
get 'url_helper_controller_test/url_helper/show_named_route',
- :to => 'url_helper_controller_test/url_helper#show_named_route',
- :as => :show_named_route
+ to: 'url_helper_controller_test/url_helper#show_named_route',
+ as: :show_named_route
get "/:controller(/:action(/:id))"
get 'url_helper_controller_test/url_helper/normalize_recall_params',
- :to => UrlHelperController.action(:normalize_recall),
- :as => :normalize_recall_params
+ to: UrlHelperController.action(:normalize_recall),
+ as: :normalize_recall_params
get '/url_helper_controller_test/url_helper/override_url_helper/default',
- :to => 'url_helper_controller_test/url_helper#override_url_helper',
- :as => :override_url_helper
+ to: 'url_helper_controller_test/url_helper#override_url_helper',
+ as: :override_url_helper
end
def show
if params[:name]
- render :inline => 'ok'
+ render inline: 'ok'
else
redirect_to profile_path(params[:id])
end
end
def show_url_for
- render :inline => "<%= url_for :controller => 'url_helper_controller_test/url_helper', :action => 'show_url_for' %>"
+ render inline: "<%= url_for controller: 'url_helper_controller_test/url_helper', action: 'show_url_for' %>"
end
def show_overriden_url_for
- render :inline => "<%= url_for params.merge(:controller => 'url_helper_controller_test/url_helper', :action => 'show_url_for') %>"
+ render inline: "<%= url_for params.merge(controller: 'url_helper_controller_test/url_helper', action: 'show_url_for') %>"
end
def show_named_route
- render :inline => "<%= show_named_route_#{params[:kind]} %>"
+ render inline: "<%= show_named_route_#{params[:kind]} %>"
end
def nil_url_for
- render :inline => '<%= url_for(nil) %>'
+ render inline: '<%= url_for(nil) %>'
end
def normalize_recall_params
- render :inline => '<%= normalize_recall_params_path %>'
+ render inline: '<%= normalize_recall_params_path %>'
end
def recall_params_not_changed
- render :inline => '<%= url_for(:action => :show_url_for) %>'
+ render inline: '<%= url_for(action: :show_url_for) %>'
end
def override_url_helper
- render :inline => '<%= override_url_helper_path %>'
+ render inline: '<%= override_url_helper_path %>'
end
def override_url_helper_path
@@ -645,13 +690,13 @@ class UrlHelperControllerTest < ActionController::TestCase
end
def test_named_route_url_shows_host_and_path
- get :show_named_route, :kind => 'url'
+ get :show_named_route, kind: 'url'
assert_equal 'http://test.host/url_helper_controller_test/url_helper/show_named_route',
@response.body
end
def test_named_route_path_shows_only_path
- get :show_named_route, :kind => 'path'
+ get :show_named_route, kind: 'path'
assert_equal '/url_helper_controller_test/url_helper/show_named_route', @response.body
end
@@ -663,11 +708,11 @@ class UrlHelperControllerTest < ActionController::TestCase
def test_named_route_should_show_host_and_path_using_controller_default_url_options
class << @controller
def default_url_options
- {:host => 'testtwo.host'}
+ { host: 'testtwo.host' }
end
end
- get :show_named_route, :kind => 'url'
+ get :show_named_route, kind: 'url'
assert_equal 'http://testtwo.host/url_helper_controller_test/url_helper/show_named_route', @response.body
end
@@ -682,11 +727,11 @@ class UrlHelperControllerTest < ActionController::TestCase
end
def test_recall_params_should_normalize_id
- get :show, :id => '123'
+ get :show, id: '123'
assert_equal 302, @response.status
assert_equal 'http://test.host/url_helper_controller_test/url_helper/profile/123', @response.location
- get :show, :name => '123'
+ get :show, name: '123'
assert_equal 'ok', @response.body
end
@@ -711,9 +756,8 @@ class TasksController < ActionController::Base
protected
def render_default
- render :inline =>
- "<%= link_to_unless_current(\"tasks\", tasks_path) %>\n" +
- "<%= link_to_unless_current(\"tasks\", tasks_url) %>"
+ render inline: "<%= link_to_unless_current('tasks', tasks_path) %>\n" +
+ "<%= link_to_unless_current('tasks', tasks_url) %>"
end
end
@@ -726,9 +770,9 @@ class LinkToUnlessCurrentWithControllerTest < ActionController::TestCase
end
def test_link_to_unless_current_shows_link
- get :show, :id => 1
- assert_equal "<a href=\"/tasks\">tasks</a>\n" +
- "<a href=\"#{@request.protocol}#{@request.host_with_port}/tasks\">tasks</a>",
+ get :show, id: 1
+ assert_equal %{<a href="/tasks">tasks</a>\n} +
+ %{<a href="#{@request.protocol}#{@request.host_with_port}/tasks">tasks</a>},
@response.body
end
end
@@ -760,12 +804,12 @@ class WorkshopsController < ActionController::Base
def index
@workshop = Workshop.new(nil)
- render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
+ render inline: "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
end
def show
@workshop = Workshop.new(params[:id])
- render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
+ render inline: "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
end
end
@@ -779,13 +823,13 @@ class SessionsController < ActionController::Base
def index
@workshop = Workshop.new(params[:workshop_id])
@session = Session.new(nil)
- render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
+ render inline: "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
end
def show
@workshop = Workshop.new(params[:workshop_id])
@session = Session.new(params[:id])
- render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
+ render inline: "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
end
end
@@ -794,27 +838,27 @@ class PolymorphicControllerTest < ActionController::TestCase
@controller = WorkshopsController.new
get :index
- assert_equal "/workshops\n<a href=\"/workshops\">Workshop</a>", @response.body
+ assert_equal %{/workshops\n<a href="/workshops">Workshop</a>}, @response.body
end
def test_existing_resource
@controller = WorkshopsController.new
- get :show, :id => 1
- assert_equal "/workshops/1\n<a href=\"/workshops/1\">Workshop</a>", @response.body
+ get :show, id: 1
+ assert_equal %{/workshops/1\n<a href="/workshops/1">Workshop</a>}, @response.body
end
def test_new_nested_resource
@controller = SessionsController.new
- get :index, :workshop_id => 1
- assert_equal "/workshops/1/sessions\n<a href=\"/workshops/1/sessions\">Session</a>", @response.body
+ get :index, workshop_id: 1
+ assert_equal %{/workshops/1/sessions\n<a href="/workshops/1/sessions">Session</a>}, @response.body
end
def test_existing_nested_resource
@controller = SessionsController.new
- get :show, :workshop_id => 1, :id => 1
- assert_equal "/workshops/1/sessions/1\n<a href=\"/workshops/1/sessions/1\">Session</a>", @response.body
+ get :show, workshop_id: 1, id: 1
+ assert_equal %{/workshops/1/sessions/1\n<a href="/workshops/1/sessions/1">Session</a>}, @response.body
end
end
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 2c966943ee..aa42bf762f 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -76,6 +76,6 @@
* Trim down Active Model API by removing `valid?` and `errors.full_messages` *José Valim*
-* When `^` or `$` are used in the regular expression provided to `validates_format_of` and the :multiline option is not set to true, an exception will be raised. This is to prevent security vulnerabilities when using `validates_format_of`. The problem is described in detail in the Rails security guide.
+* When `^` or `$` are used in the regular expression provided to `validates_format_of` and the :multiline option is not set to true, an exception will be raised. This is to prevent security vulnerabilities when using `validates_format_of`. The problem is described in detail in the Rails security guide *Jan Berdajs + Egor Homakov*
Please check [3-2-stable](https://github.com/rails/rails/blob/3-2-stable/activemodel/CHANGELOG.md) for previous changes.
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc
index b4565b5881..fc34d69ff6 100644
--- a/activemodel/README.rdoc
+++ b/activemodel/README.rdoc
@@ -23,7 +23,7 @@ to integrate with Action Pack out of the box: <tt>ActiveModel::Model</tt>.
validates_presence_of :name
end
- person = Person.new(:name => 'bob', :age => '18')
+ person = Person.new(name: 'bob', age: '18')
person.name # => 'bob'
person.age # => '18'
person.valid? # => true
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 4762f39044..9e6ec17ce1 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -3,6 +3,7 @@ require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/hash/except'
require 'active_model/errors'
require 'active_model/validations/callbacks'
+require 'active_model/validator'
module ActiveModel
@@ -188,9 +189,9 @@ module ActiveModel
# # #<ActiveModel::Validations::InclusionValidator:0x007fe603bb8780 @attributes=[:age], @options={:in=>0..99}>
# # ]
def validators_on(*attributes)
- attributes.map do |attribute|
+ attributes.flat_map do |attribute|
_validators[attribute.to_sym]
- end.flatten
+ end
end
# Returns +true+ if +attribute+ is an attribute method, +false+ otherwise.
@@ -233,7 +234,7 @@ module ActiveModel
#
# person = Person.new
# person.valid? # => false
- # person.errors # => #<ActiveModel::Errors:0x007fe603816640 @messages={:name=>["can't be blank"]}>
+ # person.errors # => #<ActiveModel::Errors:0x007fe603816640 @messages={:name=>["can't be blank"]}>
def errors
@errors ||= Errors.new(self)
end
@@ -250,7 +251,7 @@ module ActiveModel
#
# person = Person.new
# person.name = ''
- # person.valid? # => false
+ # person.valid? # => false
# person.name = 'david'
# person.valid? # => true
#
@@ -265,7 +266,7 @@ module ActiveModel
# end
#
# person = Person.new
- # person.valid? # => true
+ # person.valid? # => true
# person.valid?(:new) # => false
def valid?(context = nil)
current_context, self.validation_context = validation_context, context
@@ -287,7 +288,7 @@ module ActiveModel
#
# person = Person.new
# person.name = ''
- # person.invalid? # => true
+ # person.invalid? # => true
# person.name = 'david'
# person.invalid? # => false
#
@@ -302,7 +303,7 @@ module ActiveModel
# end
#
# person = Person.new
- # person.invalid? # => false
+ # person.invalid? # => false
# person.invalid?(:new) # => true
def invalid?(context = nil)
!valid?(context)
diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb
index 19e74d3cc9..509e2fdbb5 100644
--- a/activemodel/test/cases/secure_password_test.rb
+++ b/activemodel/test/cases/secure_password_test.rb
@@ -55,7 +55,7 @@ class SecurePasswordTest < ActiveModel::TestCase
end
test "User should not be created with blank digest" do
- assert_raise RuntimeError do
+ assert_raise RuntimeError do
@user.run_callbacks :create
end
@user.password = "supersecretpassword"
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 67bec5f38e..ffd19a5334 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,106 @@
## Rails 4.0.0 (unreleased) ##
+* Add `find_or_create_by`, `find_or_create_by!` and
+ `find_or_initialize_by` methods to `Relation`.
+
+ These are similar to the `first_or_create` family of methods, but
+ the behaviour when a record is created is slightly different:
+
+ User.where(first_name: 'Penélope').first_or_create
+
+ will execute:
+
+ User.where(first_name: 'Penélope').create
+
+ Causing all the `create` callbacks to execute within the context of
+ the scope. This could affect queries that occur within callbacks.
+
+ User.find_or_create_by(first_name: 'Penélope')
+
+ will execute:
+
+ User.create(first_name: 'Penélope')
+
+ Which obviously does not affect the scoping of queries within
+ callbacks.
+
+ The `find_or_create_by` version also reads better, frankly.
+
+ If you need to add extra attributes during create, you can do one of:
+
+ User.create_with(active: true).find_or_create_by(first_name: 'Jon')
+ User.find_or_create_by(first_name: 'Jon') { |u| u.active = true }
+
+ The `first_or_create` family of methods have been nodoc'ed in favour
+ of this API. They may be deprecated in the future but their
+ implementation is very small and it's probably not worth putting users
+ through lots of annoying deprecation warnings.
+
+ *Jon Leighton*
+
+* Fix bug with presence validation of associations. Would incorrectly add duplicated errors
+ when the association was blank. Bug introduced in 1fab518c6a75dac5773654646eb724a59741bc13.
+
+ *Scott Willson*
+
+* Fix bug where sum(expression) returns string '0' for no matching records
+ Fixes #7439
+
+ *Tim Macfarlane*
+
+* PostgreSQL adapter correctly fetches default values when using multiple schemas and domains in a db. Fixes #7914
+
+ *Arturo Pie*
+
+* Learn ActiveRecord::QueryMethods#order work with hash arguments
+
+ When symbol or hash passed we convert it to Arel::Nodes::Ordering.
+ If we pass invalid direction(like name: :DeSc) ActiveRecord::QueryMethods#order will raise an exception
+
+ User.order(:name, email: :desc)
+ # SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
+
+ *Tima Maslyuchenko*
+
+* Rename `ActiveRecord::Fixtures` class to `ActiveRecord::FixtureSet`.
+ Instances of this class normally hold a collection of fixtures (records)
+ loaded either from a single YAML file, or from a file and a folder
+ with the same name. This change make the class name singular and makes
+ the class easier to distinguish from the modules like
+ `ActiveRecord::TestFixtures`, which operates on multiple fixture sets,
+ or `DelegatingFixtures`, `::Fixtures`, etc.,
+ and from the class `ActiveRecord::Fixture`, which corresponds to a single
+ fixture.
+
+ *Alexey Muranov*
+
+* The postgres adapter now supports tables with capital letters.
+ Fix #5920
+
+ *Yves Senn*
+
+* `CollectionAssociation#count` returns `0` without querying if the
+ parent record is not persisted.
+
+ Before:
+
+ person.pets.count
+ # SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" IS NULL
+ # => 0
+
+ After:
+
+ person.pets.count
+ # fires without sql query
+ # => 0
+
+ *Francesco Rodriguez*
+
+* Fix `reset_counters` crashing on `has_many :through` associations.
+ Fix #7822.
+
+ *lulalala*
+
* Support for partial inserts.
When inserting new records, only the fields which have been changed
@@ -11,6 +112,10 @@
app processes (so long as the code in those processes doesn't
contain any references to the removed column).
+ The `partial_updates` configuration option is now renamed to
+ `partial_writes` to reflect the fact that it now impacts both inserts
+ and updates.
+
*Jon Leighton*
* Allow before and after validations to take an array of lifecycle events
@@ -230,6 +335,11 @@
*Ari Pollak*
+* Fix AR#dup to nullify the validation errors in the dup'ed object. Previously the original
+ and the dup'ed object shared the same errors.
+
+ * Christian Seiler*
+
* Raise `ArgumentError` if list of attributes to change is empty in `update_all`.
*Roman Shatsov*
@@ -539,9 +649,9 @@
* `find_or_initialize_by_...` can be rewritten using
`where(...).first_or_initialize`
* `find_or_create_by_...` can be rewritten using
- `where(...).first_or_create`
+ `find_or_create_by(...)` or where(...).first_or_create`
* `find_or_create_by_...!` can be rewritten using
- `where(...).first_or_create!`
+ `find_or_create_by!(...) or `where(...).first_or_create!`
The implementation of the deprecated dynamic finders has been moved
to the `activerecord-deprecated_finders` gem. See below for details.
@@ -608,7 +718,7 @@
*Michael Pearson*
* Added default order to `first` to assure consistent results among
- diferent database engines. Introduced `take` as a replacement to
+ different database engines. Introduced `take` as a replacement to
the old behavior of `first`.
*Marcelo Silveira*
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 258d602afa..69b95f814c 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -190,10 +190,10 @@ module ActiveRecord
# * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
# * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
# * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
- # <tt>Project#milestones.delete(milestone), Project#milestones.find(milestone_id), Project#milestones.all(options),</tt>
- # <tt>Project#milestones.build, Project#milestones.create</tt>
+ # <tt>Project#milestones.delete(milestone), Project#milestones.destroy(mileston), Project#milestones.find(milestone_id),</tt>
+ # <tt>Project#milestones.all(options), Project#milestones.build, Project#milestones.create</tt>
# * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
- # <tt>Project#categories.delete(category1)</tt>
+ # <tt>Project#categories.delete(category1), Project#categories.destroy(category1)</tt>
#
# === A word of warning
#
@@ -236,6 +236,7 @@ module ActiveRecord
# others.clear | X | X | X
# others.delete(other,other,...) | X | X | X
# others.delete_all | X | X | X
+ # others.destroy(other,other,...) | X | X | X
# others.destroy_all | X | X | X
# others.find(*args) | X | X | X
# others.exists? | X | X | X
@@ -1031,6 +1032,12 @@ module ActiveRecord
# If the <tt>:through</tt> option is used, then the join records are deleted (rather than
# nullified) by default, but you can specify <tt>:dependent => :destroy</tt> or
# <tt>:dependent => :nullify</tt> to override this.
+ # [collection.destroy(object, ...)]
+ # Removes one or more objects from the collection by running <tt>destroy</tt> on
+ # each record, regardless of any dependent option, ensuring callbacks are run.
+ #
+ # If the <tt>:through</tt> option is used, then the join records are destroyed
+ # instead, not the objects themselves.
# [collection=objects]
# Replaces the collections content by deleting and adding objects as appropriate. If the <tt>:through</tt>
# option is true callbacks in the join models are triggered except destroy callbacks, since deletion is
@@ -1074,6 +1081,7 @@ module ActiveRecord
# * <tt>Firm#clients</tt> (similar to <tt>Clients.all :conditions => ["firm_id = ?", id]</tt>)
# * <tt>Firm#clients<<</tt>
# * <tt>Firm#clients.delete</tt>
+ # * <tt>Firm#clients.destroy</tt>
# * <tt>Firm#clients=</tt>
# * <tt>Firm#client_ids</tt>
# * <tt>Firm#client_ids=</tt>
@@ -1425,6 +1433,9 @@ module ActiveRecord
# [collection.delete(object, ...)]
# Removes one or more objects from the collection by removing their associations from the join table.
# This does not destroy the objects.
+ # [collection.destroy(object, ...)]
+ # Removes one or more objects from the collection by running destroy on each association in the join table, overriding any dependent option.
+ # This does not destroy the objects.
# [collection=objects]
# Replaces the collection's content by deleting and adding objects as appropriate.
# [collection_singular_ids]
@@ -1461,6 +1472,7 @@ module ActiveRecord
# * <tt>Developer#projects</tt>
# * <tt>Developer#projects<<</tt>
# * <tt>Developer#projects.delete</tt>
+ # * <tt>Developer#projects.destroy</tt>
# * <tt>Developer#projects=</tt>
# * <tt>Developer#project_ids</tt>
# * <tt>Developer#project_ids=</tt>
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index fe3e5b00f7..7f39d3083e 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -174,6 +174,8 @@ module ActiveRecord
# association, it will be used for the query. Otherwise, construct options and pass them with
# scope to the target class's +count+.
def count(column_name = nil, count_options = {})
+ return 0 if owner.new_record?
+
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
if options[:counter_sql] || options[:finder_sql]
@@ -412,7 +414,7 @@ module ActiveRecord
persisted.map! do |record|
if mem_record = memory.delete(record)
- (record.attribute_names - mem_record.changes.keys).each do |name|
+ ((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name|
mem_record[name] = record[name]
end
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index ce5bf15f10..c1cd3a4ae3 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -30,17 +30,21 @@ module ActiveRecord
# option references an association's column), it will fallback to the table
# join strategy.
class Preloader #:nodoc:
- autoload :Association, 'active_record/associations/preloader/association'
- autoload :SingularAssociation, 'active_record/associations/preloader/singular_association'
- autoload :CollectionAssociation, 'active_record/associations/preloader/collection_association'
- autoload :ThroughAssociation, 'active_record/associations/preloader/through_association'
+ extend ActiveSupport::Autoload
- autoload :HasMany, 'active_record/associations/preloader/has_many'
- autoload :HasManyThrough, 'active_record/associations/preloader/has_many_through'
- autoload :HasOne, 'active_record/associations/preloader/has_one'
- autoload :HasOneThrough, 'active_record/associations/preloader/has_one_through'
- autoload :HasAndBelongsToMany, 'active_record/associations/preloader/has_and_belongs_to_many'
- autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
+ eager_autoload do
+ autoload :Association, 'active_record/associations/preloader/association'
+ autoload :SingularAssociation, 'active_record/associations/preloader/singular_association'
+ autoload :CollectionAssociation, 'active_record/associations/preloader/collection_association'
+ autoload :ThroughAssociation, 'active_record/associations/preloader/through_association'
+
+ autoload :HasMany, 'active_record/associations/preloader/has_many'
+ autoload :HasManyThrough, 'active_record/associations/preloader/has_many_through'
+ autoload :HasOne, 'active_record/associations/preloader/has_one'
+ autoload :HasOneThrough, 'active_record/associations/preloader/has_one_through'
+ autoload :HasAndBelongsToMany, 'active_record/associations/preloader/has_and_belongs_to_many'
+ autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
+ end
attr_reader :records, :associations, :preload_scope, :model
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index af13b75a9d..6c5e2ac05d 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -57,9 +57,8 @@ module ActiveRecord
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
- # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum,
- # f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the
- # attribute will be set to +nil+.
+ # parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum and
+ # f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
def assign_multiparameter_attributes(pairs)
execute_callstack_for_multiparameter_attributes(
extract_callstack_for_multiparameter_attributes(pairs)
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 7a5bb9e863..59f209cec8 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -1,9 +1,10 @@
require 'active_support/core_ext/module/attribute_accessors'
+require 'active_support/deprecation'
module ActiveRecord
ActiveSupport.on_load(:active_record_config) do
- mattr_accessor :partial_updates, instance_accessor: false
- self.partial_updates = true
+ mattr_accessor :partial_writes, instance_accessor: false
+ self.partial_writes = true
end
module AttributeMethods
@@ -17,7 +18,18 @@ module ActiveRecord
raise "You cannot include Dirty after Timestamp"
end
- config_attribute :partial_updates
+ config_attribute :partial_writes
+
+ def self.partial_updates=(v); self.partial_writes = v; end
+ def self.partial_updates?; partial_writes?; end
+ def self.partial_updates; partial_writes; end
+
+ ActiveSupport::Deprecation.deprecate_methods(
+ singleton_class,
+ :partial_updates= => :partial_writes=,
+ :partial_updates? => :partial_writes?,
+ :partial_updates => :partial_writes
+ )
end
# Attempts to +save+ the record and clears changed attributes if successful.
@@ -64,26 +76,16 @@ module ActiveRecord
end
def update(*)
- partial_updates? ? super(keys_for_partial_update) : super
+ partial_writes? ? super(keys_for_partial_write) : super
end
def create(*)
- if partial_updates?
- keys = keys_for_partial_update
-
- # This is an extremely bloody annoying necessity to work around mysql being crap.
- # See test_mysql_text_not_null_defaults
- keys.concat self.class.columns.select(&:explicit_default?).map(&:name)
-
- super keys
- else
- super
- end
+ partial_writes? ? super(keys_for_partial_write) : super
end
# Serialized attributes should always be written in case they've been
# changed in place.
- def keys_for_partial_update
+ def keys_for_partial_write
changed | (attributes.keys & self.class.serialized_attributes.keys)
end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 6213b5dcd5..46fd6ebfb3 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -35,21 +35,36 @@ module ActiveRecord
protected
- # We want to generate the methods via module_eval rather than define_method,
- # because define_method is slower on dispatch and uses more memory (because it
- # creates a closure).
+ # We want to generate the methods via module_eval rather than
+ # define_method, because define_method is slower on dispatch and
+ # uses more memory (because it creates a closure).
#
- # But sometimes the database might return columns with characters that are not
- # allowed in normal method names (like 'my_column(omg)'. So to work around this
- # we first define with the __temp__ identifier, and then use alias method to
- # rename it to what we want.
- def define_method_attribute(attr_name)
+ # But sometimes the database might return columns with
+ # characters that are not allowed in normal method names (like
+ # 'my_column(omg)'. So to work around this we first define with
+ # the __temp__ identifier, and then use alias method to rename
+ # it to what we want.
+ #
+ # We are also defining a constant to hold the frozen string of
+ # the attribute name. Using a constant means that we do not have
+ # to allocate an object on each call to the attribute method.
+ # Making it frozen means that it doesn't get duped when used to
+ # key the @attributes_cache in read_attribute.
+ def define_method_attribute(name)
+ safe_name = name.unpack('h*').first
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
- def __temp__
- read_attribute(:'#{attr_name}') { |n| missing_attribute(n, caller) }
+ module AttrNames
+ unless defined? ATTR_#{safe_name}
+ ATTR_#{safe_name} = #{name.inspect}.freeze
+ end
+ end
+
+ def __temp__#{safe_name}
+ read_attribute(AttrNames::ATTR_#{safe_name}) { |n| missing_attribute(n, caller) }
end
- alias_method '#{attr_name}', :__temp__
- undef_method :__temp__
+
+ alias_method #{name.inspect}, :__temp__#{safe_name}
+ undef_method :__temp__#{safe_name}
STR
end
@@ -70,17 +85,13 @@ module ActiveRecord
# it has been typecast (for example, "2004-12-12" in a data column is cast
# to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name)
- return unless attr_name
- name_sym = attr_name.to_sym
-
# If it's cached, just return it
# We use #[] first as a perf optimization for non-nil values. See https://gist.github.com/3552829.
- @attributes_cache[name_sym] || @attributes_cache.fetch(name_sym) {
- name = attr_name.to_s
-
+ name = attr_name.to_s
+ @attributes_cache[name] || @attributes_cache.fetch(name) {
column = @columns_hash.fetch(name) {
return @attributes.fetch(name) {
- if name_sym == :id && self.class.primary_key != name
+ if name == 'id' && self.class.primary_key != name
read_attribute(self.class.primary_key)
end
}
@@ -91,7 +102,7 @@ module ActiveRecord
}
if self.class.cache_attribute?(name)
- @attributes_cache[name_sym] = column.type_cast(value)
+ @attributes_cache[name] = column.type_cast(value)
else
column.type_cast value
end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index b9a69cdb0a..f36a5806a9 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -50,7 +50,7 @@ module ActiveRecord
if (rounded_value != rounded_time) || (!rounded_value && original_time)
write_attribute("#{attr_name}", original_time)
#{attr_name}_will_change!
- @attributes_cache[:"#{attr_name}"] = zoned_time
+ @attributes_cache["#{attr_name}"] = zoned_time
end
end
EOV
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index 6eb9e25fd9..cd33494cc3 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -9,15 +9,19 @@ module ActiveRecord
module ClassMethods
protected
- def define_method_attribute=(attr_name)
- if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP
- generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__)
- else
- generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
- write_attribute(attr_name, new_value)
- end
+
+ # See define_method_attribute in read.rb for an explanation of
+ # this code.
+ def define_method_attribute=(name)
+ safe_name = name.unpack('h*').first
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
+ def __temp__#{safe_name}=(value)
+ write_attribute(AttrNames::ATTR_#{safe_name}, value)
end
- end
+ alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
+ undef_method :__temp__#{safe_name}=
+ STR
+ end
end
# Updates the attribute identified by <tt>attr_name</tt> with the
@@ -26,13 +30,13 @@ module ActiveRecord
def write_attribute(attr_name, value)
attr_name = attr_name.to_s
attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
- @attributes_cache.delete(attr_name.to_sym)
+ @attributes_cache.delete(attr_name)
column = column_for_attribute(attr_name)
# If we're dealing with a binary column, write the data to the cache
# so we don't attempt to typecast multiple times.
if column && column.binary?
- @attributes_cache[attr_name.to_sym] = value
+ @attributes_cache[attr_name] = value
end
if column || @attributes.has_key?(attr_name)
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 8c83c4f5db..b0e7bd7e82 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -4,17 +4,19 @@ module ActiveRecord
module ConnectionAdapters
class AbstractMysqlAdapter < AbstractAdapter
class Column < ConnectionAdapters::Column # :nodoc:
- attr_reader :collation
+ attr_reader :collation, :strict
- def initialize(name, default, sql_type = nil, null = true, collation = nil)
- super(name, default, sql_type, null)
+ def initialize(name, default, sql_type = nil, null = true, collation = nil, strict = false)
+ @strict = strict
@collation = collation
+
+ super(name, default, sql_type, null)
end
def extract_default(default)
if sql_type =~ /blob/i || type == :text
if default.blank?
- return null ? nil : ''
+ null || strict ? nil : ''
else
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
end
@@ -30,10 +32,6 @@ module ActiveRecord
super
end
- def explicit_default?
- !null && (sql_type =~ /blob/i || type == :text)
- end
-
# Must return the relevant concrete adapter
def adapter
raise NotImplementedError
@@ -571,6 +569,10 @@ module ActiveRecord
where_sql
end
+ def strict_mode?
+ @config.fetch(:strict, true)
+ end
+
protected
# MySQL is too stupid to create a temporary table for use subquery, so we have
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 2028abf6f0..816b5e17c1 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -53,10 +53,6 @@ module ActiveRecord
!default.nil?
end
- def explicit_default?
- false
- end
-
# Returns the Ruby class that corresponds to the abstract data type.
def klass
case type
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 328d080687..879eec7fcf 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -53,7 +53,7 @@ module ActiveRecord
end
def new_column(field, default, type, null, collation) # :nodoc:
- Column.new(field, default, type, null, collation)
+ Column.new(field, default, type, null, collation, strict_mode?)
end
def error_number(exception)
@@ -259,9 +259,7 @@ module ActiveRecord
# Make MySQL reject illegal values rather than truncating or
# blanking them. See
# http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html#sqlmode_strict_all_tables
- if @config.fetch(:strict, true)
- variable_assignments << "SQL_MODE='STRICT_ALL_TABLES'"
- end
+ variable_assignments << "SQL_MODE='STRICT_ALL_TABLES'" if strict_mode?
encoding = @config[:encoding]
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 0b936bbf39..76667616a1 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -150,7 +150,7 @@ module ActiveRecord
end
def new_column(field, default, type, null, collation) # :nodoc:
- Column.new(field, default, type, null, collation)
+ Column.new(field, default, type, null, collation, strict_mode?)
end
def error_number(exception) # :nodoc:
@@ -546,9 +546,7 @@ module ActiveRecord
# Make MySQL reject illegal values rather than truncating or
# blanking them. See
# http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html#sqlmode_strict_all_tables
- if @config.fetch(:strict, true)
- execute("SET SQL_MODE='STRICT_ALL_TABLES'", :skip_logging)
- end
+ execute("SET SQL_MODE='STRICT_ALL_TABLES'", :skip_logging) if strict_mode?
end
def select(sql, name = nil, binds = [])
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index 37d43d891d..9d3fa18e3a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -90,7 +90,7 @@ module ActiveRecord
else super(value, column)
end
when IPAddr
- return super(value, column) unless ['inet','cidr'].includes? column.sql_type
+ return super(value, column) unless ['inet','cidr'].include? column.sql_type
PostgreSQLColumn.cidr_to_string(value)
else
super(value, column)
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 8a073bf878..7cad8f94cf 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -280,16 +280,13 @@ module ActiveRecord
end_sql
if result.nil? or result.empty?
- # If that fails, try parsing the primary key's default value.
- # Support the 7.x and 8.0 nextval('foo'::text) as well as
- # the 8.1+ nextval('foo'::regclass).
result = query(<<-end_sql, 'SCHEMA')[0]
SELECT attr.attname,
CASE
- WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
- substr(split_part(def.adsrc, '''', 2),
- strpos(split_part(def.adsrc, '''', 2), '.')+1)
- ELSE split_part(def.adsrc, '''', 2)
+ WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
+ substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
+ strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
+ ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
END
FROM pg_class t
JOIN pg_attribute attr ON (t.oid = attrelid)
@@ -297,7 +294,7 @@ module ActiveRecord
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
WHERE t.oid = '#{quote_table_name(table)}'::regclass
AND cons.contype = 'p'
- AND def.adsrc ~* 'nextval'
+ AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval'
end_sql
end
@@ -314,7 +311,7 @@ module ActiveRecord
INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
WHERE cons.contype = 'p'
- AND dep.refobjid = '#{table}'::regclass
+ AND dep.refobjid = '#{quote_table_name(table)}'::regclass
end_sql
row && row.first
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 5e35f472c7..e18464fa35 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -78,11 +78,8 @@ module ActiveRecord
when /\A\(?(-?\d+(\.\d*)?\)?)\z/
$1
# Character types
- when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
+ when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
$1
- # Character types (8.1 formatting)
- when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
- $1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
# Binary data types
when /\A'(.*)'::bytea\z/m
$1
@@ -241,7 +238,7 @@ module ActiveRecord
# <encoding></tt> call on the connection.
# * <tt>:min_messages</tt> - An optional client min messages that is used in a
# <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
- # * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT<tt> statements
+ # * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
# defaults to true.
#
# Any further options are used as connection parameters to libpq. See
@@ -763,7 +760,8 @@ module ActiveRecord
# - ::regclass is a function that gives the id for a table name
def column_definitions(table_name) #:nodoc:
exec_query(<<-end_sql, 'SCHEMA').rows
- SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull, a.atttypid, a.atttypmod
+ SELECT a.attname, format_type(a.atttypid, a.atttypmod),
+ pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index c877079b25..57838ff984 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -22,6 +22,10 @@ module ActiveRecord
counters.each do |association|
has_many_association = reflect_on_association(association.to_sym)
+ if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
+ has_many_association = has_many_association.through_reflection
+ end
+
foreign_key = has_many_association.foreign_key.to_s
child_class = has_many_association.klass
belongs_to = child_class.reflect_on_all_associations(:belongs_to)
diff --git a/activerecord/lib/active_record/fixtures/file.rb b/activerecord/lib/active_record/fixture_set/file.rb
index 0f6ab3e396..11b53275e1 100644
--- a/activerecord/lib/active_record/fixtures/file.rb
+++ b/activerecord/lib/active_record/fixture_set/file.rb
@@ -2,7 +2,7 @@ require 'erb'
require 'yaml'
module ActiveRecord
- class Fixtures
+ class FixtureSet
class File # :nodoc:
include Enumerable
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 60fc653735..413bd147de 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -2,9 +2,11 @@ require 'erb'
require 'yaml'
require 'zlib'
require 'active_support/dependencies'
-require 'active_record/fixtures/file'
+require 'active_record/fixture_set/file'
require 'active_record/errors'
+require 'active_support/deprecation' # temporary
+
module ActiveRecord
class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
end
@@ -350,8 +352,8 @@ module ActiveRecord
# to the rescue:
#
# george_reginald:
- # monkey_id: <%= ActiveRecord::Fixtures.identify(:reginald) %>
- # pirate_id: <%= ActiveRecord::Fixtures.identify(:george) %>
+ # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
+ # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
#
# == Support for YAML defaults
#
@@ -370,10 +372,11 @@ module ActiveRecord
# *DEFAULTS
#
# Any fixture labeled "DEFAULTS" is safely ignored.
- class Fixtures
+ class FixtureSet
#--
- # NOTE: an instance of Fixtures can be called fixture_set, it is normally stored in a single YAML file and possibly in a folder with the same name.
+ # An instance of FixtureSet is normally stored in a single YAML file and possibly in a folder with the same name.
#++
+
MAX_ID = 2 ** 30 - 1
@@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
@@ -451,7 +454,7 @@ module ActiveRecord
fixtures_map = {}
fixture_sets = files_to_read.map do |fs_name|
- fixtures_map[fs_name] = new( # ActiveRecord::Fixtures.new
+ fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
connection,
fs_name,
class_names[fs_name] || default_fixture_model_name(fs_name),
@@ -565,7 +568,7 @@ module ActiveRecord
# generate a primary key if necessary
if has_primary_key_column? && !row.include?(primary_key_name)
- row[primary_key_name] = ActiveRecord::Fixtures.identify(label)
+ row[primary_key_name] = ActiveRecord::FixtureSet.identify(label)
end
# If STI is used, find the correct subclass for association reflection
@@ -588,7 +591,7 @@ module ActiveRecord
row[association.foreign_type] = $1
end
- row[fk_name] = ActiveRecord::Fixtures.identify(value)
+ row[fk_name] = ActiveRecord::FixtureSet.identify(value)
end
when :has_and_belongs_to_many
if (targets = row.delete(association.name.to_s))
@@ -596,7 +599,7 @@ module ActiveRecord
table_name = association.join_table
rows[table_name].concat targets.map { |target|
{ association.foreign_key => row[primary_key_name],
- association.association_foreign_key => ActiveRecord::Fixtures.identify(target) }
+ association.association_foreign_key => ActiveRecord::FixtureSet.identify(target) }
}
end
end
@@ -637,7 +640,7 @@ module ActiveRecord
} + [yaml_file_path]
yaml_files.each do |file|
- Fixtures::File.open(file) do |fh|
+ FixtureSet::File.open(file) do |fh|
fh.each do |fixture_name, row|
fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
end
@@ -651,6 +654,11 @@ module ActiveRecord
end
+ #--
+ # Deprecate 'Fixtures' in favor of 'FixtureSet'.
+ #++
+ Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::Fixtures', 'ActiveRecord::FixtureSet')
+
class Fixture #:nodoc:
include Enumerable
@@ -712,7 +720,7 @@ module ActiveRecord
self.pre_loaded_fixtures = false
self.fixture_class_names = Hash.new do |h, fixture_set_name|
- h[fixture_set_name] = ActiveRecord::Fixtures.default_fixture_model_name(fixture_set_name)
+ h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name)
end
end
@@ -847,7 +855,7 @@ module ActiveRecord
end
# Load fixtures for every test.
else
- ActiveRecord::Fixtures.reset_cache
+ ActiveRecord::FixtureSet.reset_cache
@@already_loaded_fixtures[self.class] = nil
@loaded_fixtures = load_fixtures
end
@@ -860,7 +868,7 @@ module ActiveRecord
return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
unless run_in_transaction?
- ActiveRecord::Fixtures.reset_cache
+ ActiveRecord::FixtureSet.reset_cache
end
# Rollback changes if a transaction is active.
@@ -879,7 +887,7 @@ module ActiveRecord
private
def load_fixtures
- fixtures = ActiveRecord::Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
Hash[fixtures.map { |f| [f.name, f] }]
end
@@ -888,16 +896,16 @@ module ActiveRecord
def instantiate_fixtures
if pre_loaded_fixtures
- raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::Fixtures.all_loaded_fixtures.empty?
+ raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
unless @@required_fixture_classes
- self.class.require_fixture_classes ActiveRecord::Fixtures.all_loaded_fixtures.keys
+ self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys
@@required_fixture_classes = true
end
- ActiveRecord::Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
+ ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
else
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
@loaded_fixtures.each_value do |fixture_set|
- ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_set, load_instances?)
+ ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
end
end
end
diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml
index 896132d566..b1fbd38622 100644
--- a/activerecord/lib/active_record/locale/en.yml
+++ b/activerecord/lib/active_record/locale/en.yml
@@ -4,11 +4,15 @@ en:
#created_at: "Created at"
#updated_at: "Updated at"
+ # Default error messages
+ errors:
+ messages:
+ taken: "has already been taken"
+
# Active Record models configuration
activerecord:
errors:
messages:
- taken: "has already been taken"
record_invalid: "Validation failed: %{errors}"
restrict_dependent_destroy:
one: "Cannot delete record because a dependent %{record} exists"
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index a25f2c7bca..ca79950049 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -1,11 +1,13 @@
module ActiveRecord
class LogSubscriber < ActiveSupport::LogSubscriber
+ IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
+
def self.runtime=(value)
- Thread.current["active_record_sql_runtime"] = value
+ Thread.current[:active_record_sql_runtime] = value
end
def self.runtime
- Thread.current["active_record_sql_runtime"] ||= 0
+ Thread.current[:active_record_sql_runtime] ||= 0
end
def self.reset_runtime
@@ -24,9 +26,9 @@ module ActiveRecord
payload = event.payload
- return if 'SCHEMA' == payload[:name]
+ return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
- name = '%s (%.1fms)' % [payload[:name], event.duration]
+ name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
sql = payload[:sql].squeeze(' ')
binds = nil
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index c1d57855a9..d5ee98382d 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -50,7 +50,7 @@ module ActiveRecord
#
# class AddSsl < ActiveRecord::Migration
# def up
- # add_column :accounts, :ssl_enabled, :boolean, :default => true
+ # add_column :accounts, :ssl_enabled, :boolean, default: true
# end
#
# def down
@@ -62,7 +62,7 @@ module ActiveRecord
# if you're backing out of the migration. It shows how all migrations have
# two methods +up+ and +down+ that describes the transformations
# required to implement or remove the migration. These methods can consist
- # of both the migration specific methods like add_column and remove_column,
+ # of both the migration specific methods like +add_column+ and +remove_column+,
# but may also contain regular Ruby code for generating data needed for the
# transformations.
#
@@ -78,9 +78,9 @@ module ActiveRecord
# t.integer :position
# end
#
- # SystemSetting.create :name => "notice",
- # :label => "Use notice?",
- # :value => 1
+ # SystemSetting.create name: 'notice',
+ # label: 'Use notice?',
+ # value: 1
# end
#
# def down
@@ -88,19 +88,22 @@ module ActiveRecord
# end
# end
#
- # This migration first adds the system_settings table, then creates the very
+ # This migration first adds the +system_settings+ table, then creates the very
# first row in it using the Active Record model that relies on the table. It
- # also uses the more advanced create_table syntax where you can specify a
+ # also uses the more advanced +create_table+ syntax where you can specify a
# complete table schema in one block call.
#
# == Available transformations
#
- # * <tt>create_table(name, options)</tt> Creates a table called +name+ and
+ # * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
# makes the table object available to a block that can then add columns to it,
- # following the same format as add_column. See example above. The options hash
+ # following the same format as +add_column+. See example above. The options hash
# is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
# table definition.
# * <tt>drop_table(name)</tt>: Drops the table called +name+.
+ # * <tt>change_table(name, options)</tt>: Allows to make column alterations to
+ # the table called +name+. It makes the table object availabe to a block that
+ # can then add/remove columns, indexes or foreign keys to it.
# * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
# to +new_name+.
# * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
@@ -109,9 +112,9 @@ module ActiveRecord
# <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
# <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
# <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
- # specified by passing an +options+ hash like <tt>{ :default => 11 }</tt>.
+ # specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
# Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
- # <tt>{ :limit => 50, :null => false }</tt>) -- see
+ # <tt>{ limit: 50, null: false }</tt>) -- see
# ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
# * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
# a column but keeps the type and content.
@@ -122,11 +125,11 @@ module ActiveRecord
# * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
# with the name of the column. Other options include
# <tt>:name</tt>, <tt>:unique</tt> (e.g.
- # <tt>{ :name => "users_name_index", :unique => true }</tt>) and <tt>:order</tt>
- # (e.g. { :order => {:name => :desc} }</tt>).
- # * <tt>remove_index(table_name, :column => column_name)</tt>: Removes the index
+ # <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
+ # (e.g. <tt>{ order: { name: :desc } }</tt>).
+ # * <tt>remove_index(table_name, column: column_name)</tt>: Removes the index
# specified by +column_name+.
- # * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
+ # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
# specified by +index_name+.
#
# == Irreversible transformations
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index f81eb5f5d1..611d3d97c3 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -197,7 +197,7 @@ module ActiveRecord
end
end
- # Updates a single attribute of an object, without calling save.
+ # Updates a single attribute of an object, without having to explicitly call save on that object.
#
# * Validation is skipped.
# * Callbacks are skipped.
@@ -209,7 +209,7 @@ module ActiveRecord
update_columns(name => value)
end
- # Updates the attributes from the passed-in hash, without calling save.
+ # Updates the attributes from the passed-in hash, without having to explicitly call save on that object.
#
# * Validation is skipped.
# * Callbacks are skipped.
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 13e09eda53..45f6a78428 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -3,6 +3,7 @@ module ActiveRecord
module Querying
delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, :to => :all
delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :all
+ delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, :to => :all
delegate :find_by, :find_by!, :to => :all
delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :all
delegate :find_each, :find_in_batches, :to => :all
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index b11483de8c..d7e35fb771 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -129,7 +129,8 @@ module ActiveRecord
end
initializer "active_record.add_watchable_files" do |app|
- config.watchable_files.concat ["#{app.root}/db/schema.rb", "#{app.root}/db/structure.sql"]
+ path = app.paths["db"].first
+ config.watchable_files.concat ["#{path}/schema.rb", "#{path}/structure.sql"]
end
config.after_initialize do |app|
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index d134978128..0a9caa25b2 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -196,7 +196,7 @@ db_namespace = namespace :db do
fixtures_dir = File.join [base_dir, ENV['FIXTURES_DIR']].compact
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file|
- ActiveRecord::Fixtures.create_fixtures(fixtures_dir, fixture_file)
+ ActiveRecord::FixtureSet.create_fixtures(fixtures_dir, fixture_file)
end
end
@@ -207,13 +207,13 @@ db_namespace = namespace :db do
label, id = ENV['LABEL'], ENV['ID']
raise 'LABEL or ID required' if label.blank? && id.blank?
- puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::Fixtures.identify(label)}.) if label
+ puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::FixtureSet.identify(label)}.) if label
base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures')
Dir["#{base_dir}/**/*.yml"].each do |file|
if data = YAML::load(ERB.new(IO.read(file)).result)
data.keys.each do |key|
- key_id = ActiveRecord::Fixtures.identify(key)
+ key_id = ActiveRecord::FixtureSet.identify(key)
if key == label || key_id == id.to_i
puts "#{file}: #{key} (#{key_id})"
@@ -307,7 +307,7 @@ db_namespace = namespace :db do
# desc "Recreate the databases from the structure.sql file"
task :load => [:environment, :load_config] do
- current_config = ActiveRecord::Tasks::DatabaseTasks.current_config(:env => (ENV['RAILS_ENV'] || 'test'))
+ current_config = ActiveRecord::Tasks::DatabaseTasks.current_config
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
case current_config['adapter']
when /mysql/, /postgresql/, /sqlite/
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index ed80422336..2e2286e4fd 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -91,8 +91,10 @@ module ActiveRecord
end
def initialize_copy(other)
- @values = @values.dup
- @values[:bind] = @values[:bind].dup if @values[:bind]
+ # This method is a hot spot, so for now, use Hash[] to dup the hash.
+ # https://bugs.ruby-lang.org/issues/7166
+ @values = Hash[@values]
+ @values[:bind] = @values[:bind].dup if @values.key? :bind
reset
end
@@ -127,46 +129,53 @@ module ActiveRecord
scoping { @klass.create!(*args, &block) }
end
- # Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
- #
- # Expects arguments in the same format as +Base.create+.
+ def first_or_create(attributes = nil, &block) # :nodoc:
+ first || create(attributes, &block)
+ end
+
+ def first_or_create!(attributes = nil, &block) # :nodoc:
+ first || create!(attributes, &block)
+ end
+
+ def first_or_initialize(attributes = nil, &block) # :nodoc:
+ first || new(attributes, &block)
+ end
+
+ # Finds the first record with the given attributes, or creates a record with the attributes
+ # if one is not found.
#
# ==== Examples
# # Find the first user named Penélope or create a new one.
- # User.where(:first_name => 'Penélope').first_or_create
+ # User.find_or_create_by(first_name: 'Penélope')
# # => <User id: 1, first_name: 'Penélope', last_name: nil>
#
# # Find the first user named Penélope or create a new one.
# # We already have one so the existing record will be returned.
- # User.where(:first_name => 'Penélope').first_or_create
+ # User.find_or_create_by(first_name: 'Penélope')
# # => <User id: 1, first_name: 'Penélope', last_name: nil>
#
# # Find the first user named Scarlett or create a new one with a particular last name.
- # User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
+ # User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
# # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
#
# # Find the first user named Scarlett or create a new one with a different last name.
# # We already have one so the existing record will be returned.
- # User.where(:first_name => 'Scarlett').first_or_create do |user|
+ # User.find_or_create_by(first_name: 'Scarlett') do |user|
# user.last_name = "O'Hara"
# end
# # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
- def first_or_create(attributes = nil, &block)
- first || create(attributes, &block)
+ def find_or_create_by(attributes, &block)
+ find_by(attributes) || create(attributes, &block)
end
- # Like <tt>first_or_create</tt> but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
- #
- # Expects arguments in the same format as <tt>Base.create!</tt>.
- def first_or_create!(attributes = nil, &block)
- first || create!(attributes, &block)
+ # Like <tt>find_or_create_by</tt>, but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
+ def find_or_create_by!(attributes, &block)
+ find_by(attributes) || create!(attributes, &block)
end
- # Like <tt>first_or_create</tt> but calls <tt>new</tt> instead of <tt>create</tt>.
- #
- # Expects arguments in the same format as <tt>Base.new</tt>.
- def first_or_initialize(attributes = nil, &block)
- first || new(attributes, &block)
+ # Like <tt>find_or_create_by</tt>, but calls <tt>new</tt> instead of <tt>create</tt>.
+ def find_or_initialize_by(attributes, &block)
+ find_by(attributes) || new(attributes, &block)
end
# Runs EXPLAIN on the query or queries triggered by this relation and
@@ -540,7 +549,7 @@ module ActiveRecord
end
def values
- @values.dup
+ Hash[@values]
end
def inspect
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 28aab6d92b..8af0c6a8ef 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -40,7 +40,7 @@ module ActiveRecord
#
# It's not possible to set the order. That is automatically set to
# ascending on the primary key ("id ASC") to make the batch ordering
- # work. This also mean that this method only works with integer-based
+ # work. This also means that this method only works with integer-based
# primary keys. You can't set the limit either, that's used to control
# the batch sizes.
#
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 7c43d844d0..a7d2f4bd24 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -343,13 +343,13 @@ module ActiveRecord
def column_for(field)
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
- @klass.columns.detect { |c| c.name.to_s == field_name }
+ @klass.columns_hash[field_name]
end
def type_cast_calculated_value(value, column, operation = nil)
case operation
when 'count' then value.to_i
- when 'sum' then type_cast_using_column(value || '0', column)
+ when 'sum' then type_cast_using_column(value || 0, column)
when 'average' then value.respond_to?(:to_d) ? value.to_d : value
else type_cast_using_column(value, column)
end
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index e5b50673da..59226d316e 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -22,7 +22,17 @@ module ActiveRecord
# the values.
def other
other = Relation.new(relation.klass, relation.table)
- hash.each { |k, v| other.send("#{k}!", v) }
+ hash.each { |k, v|
+ if k == :joins
+ if Hash === v
+ other.joins!(v)
+ else
+ other.joins!(*v)
+ end
+ else
+ other.send("#{k}!", v)
+ end
+ }
other
end
end
@@ -39,16 +49,18 @@ module ActiveRecord
@values = other.values
end
+ NORMAL_VALUES = Relation::SINGLE_VALUE_METHODS +
+ Relation::MULTI_VALUE_METHODS -
+ [:where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from] # :nodoc:
+
def normal_values
- Relation::SINGLE_VALUE_METHODS +
- Relation::MULTI_VALUE_METHODS -
- [:where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from]
+ NORMAL_VALUES
end
def merge
normal_values.each do |name|
value = values[name]
- relation.send("#{name}!", value) unless value.blank?
+ relation.send("#{name}!", *value) unless value.blank?
end
merge_multi_values
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 3c59bd8a68..14bcb337e9 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -202,6 +202,15 @@ module ActiveRecord
#
# User.order('name DESC, email')
# => SELECT "users".* FROM "users" ORDER BY name DESC, email
+ #
+ # User.order(:name)
+ # => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
+ #
+ # User.order(email: :desc)
+ # => SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
+ #
+ # User.order(:name, email: :desc)
+ # => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
def order(*args)
args.blank? ? self : spawn.order!(*args)
end
@@ -210,6 +219,8 @@ module ActiveRecord
def order!(*args)
args.flatten!
+ validate_order_args args
+
references = args.reject { |arg| Arel::Node === arg }
references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
references!(references) if references.any?
@@ -235,6 +246,8 @@ module ActiveRecord
def reorder!(*args)
args.flatten!
+ validate_order_args args
+
self.reordering_value = true
self.order_values = args
self
@@ -245,13 +258,11 @@ module ActiveRecord
# User.joins(:posts)
# => SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
def joins(*args)
- args.compact.blank? ? self : spawn.joins!(*args)
+ args.compact.blank? ? self : spawn.joins!(*args.flatten)
end
# Like #joins, but modifies relation in place.
def joins!(*args)
- args.flatten!
-
self.joins_values += args
self
end
@@ -658,9 +669,7 @@ module ActiveRecord
arel.group(*group_values.uniq.reject{|g| g.blank?}) unless group_values.empty?
- order = order_values
- order = reverse_sql_order(order) if reverse_order_value
- arel.order(*order.uniq.reject{|o| o.blank?}) unless order.empty?
+ build_order(arel)
build_select(arel, select_values.uniq)
@@ -729,22 +738,22 @@ module ActiveRecord
buckets = joins.group_by do |join|
case join
when String
- 'string_join'
+ :string_join
when Hash, Symbol, Array
- 'association_join'
+ :association_join
when ActiveRecord::Associations::JoinDependency::JoinAssociation
- 'stashed_join'
+ :stashed_join
when Arel::Nodes::Join
- 'join_node'
+ :join_node
else
raise 'unknown class: %s' % join.class.name
end
end
- association_joins = buckets['association_join'] || []
- stashed_association_joins = buckets['stashed_join'] || []
- join_nodes = (buckets['join_node'] || []).uniq
- string_joins = (buckets['string_join'] || []).map { |x|
+ association_joins = buckets[:association_join] || []
+ stashed_association_joins = buckets[:stashed_join] || []
+ join_nodes = (buckets[:join_node] || []).uniq
+ string_joins = (buckets[:string_join] || []).map { |x|
x.strip
}.uniq
@@ -786,11 +795,17 @@ module ActiveRecord
case o
when Arel::Nodes::Ordering
o.reverse
- when String, Symbol
+ when String
o.to_s.split(',').collect do |s|
s.strip!
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
end
+ when Symbol
+ { o => :desc }
+ when Hash
+ o.each_with_object({}) do |(field, dir), memo|
+ memo[field] = (dir == :asc ? :desc : :asc )
+ end
else
o
end
@@ -801,5 +816,31 @@ module ActiveRecord
o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
end
+ def build_order(arel)
+ orders = order_values
+ orders = reverse_sql_order(orders) if reverse_order_value
+
+ orders = orders.uniq.reject(&:blank?).map do |order|
+ case order
+ when Symbol
+ table[order].asc
+ when Hash
+ order.map { |field, dir| table[field].send(dir) }
+ else
+ order
+ end
+ end.flatten
+
+ arel.order(*orders) unless orders.empty?
+ end
+
+ def validate_order_args(args)
+ args.select { |a| Hash === a }.each do |h|
+ unless (h.values - [:asc, :desc]).empty?
+ raise ArgumentError, 'Direction should be :asc or :desc'
+ end
+ end
+ end
+
end
end
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index 42b4cff4b8..f3e47a958e 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -141,23 +141,6 @@ module ActiveRecord
end
end
- def expand_range_bind_variables(bind_vars) #:nodoc:
- expanded = []
-
- bind_vars.each do |var|
- next if var.is_a?(Hash)
-
- if var.is_a?(Range)
- expanded << var.first
- expanded << var.last
- else
- expanded << var
- end
- end
-
- expanded
- end
-
def quote_bound_value(value, c = connection) #:nodoc:
if value.respond_to?(:map) && !value.acts_like?(:string)
if value.respond_to?(:empty?) && value.empty?
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index bf95ccb298..ec4588f601 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -42,6 +42,7 @@ module ActiveRecord
def initialize_dup(other) # :nodoc:
clear_timestamp_attributes
+ super
end
private
@@ -74,7 +75,7 @@ module ActiveRecord
end
def should_record_timestamps?
- self.record_timestamps && (!partial_updates? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?)
+ self.record_timestamps && (!partial_writes? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?)
end
def timestamp_attributes_for_create_in_model
diff --git a/activerecord/lib/active_record/validations/presence.rb b/activerecord/lib/active_record/validations/presence.rb
index 81a3521d24..6b14c39686 100644
--- a/activerecord/lib/active_record/validations/presence.rb
+++ b/activerecord/lib/active_record/validations/presence.rb
@@ -5,8 +5,10 @@ module ActiveRecord
super
attributes.each do |attribute|
next unless record.class.reflect_on_association(attribute)
- value = record.send(attribute)
- if Array(value).all? { |r| r.marked_for_destruction? }
+ associated_records = Array(record.send(attribute))
+
+ # Superclass validates presence. Ensure present records aren't about to be destroyed.
+ if associated_records.present? && associated_records.all? { |r| r.marked_for_destruction? }
record.errors.add(attribute, :blank, options)
end
end
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 5dece1cb36..5fa6a0b892 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -26,11 +26,12 @@ module ActiveRecord
relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
Array(options[:scope]).each do |scope_item|
- scope_value = record.read_attribute(scope_item)
reflection = record.class.reflect_on_association(scope_item)
if reflection
scope_value = record.send(reflection.foreign_key)
scope_item = reflection.foreign_key
+ else
+ scope_value = record.read_attribute(scope_item)
end
relation = relation.and(table[scope_item].eq(scope_value))
end
diff --git a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
index aff971a955..5164acf77f 100644
--- a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
@@ -136,9 +136,9 @@ class MysqlReservedWordTest < ActiveRecord::TestCase
#the following functions were added to DRY test cases
private
- # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path
+ # custom fixture loader, uses FixtureSet#create_fixtures and appends base_path to the current file's path
def create_test_fixtures(*fixture_names)
- ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
+ ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
end
# custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name
diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
index 9fd07f014e..1017b0758d 100644
--- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
@@ -136,9 +136,9 @@ class MysqlReservedWordTest < ActiveRecord::TestCase
#the following functions were added to DRY test cases
private
- # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path
+ # custom fixture loader, uses FixtureSet#create_fixtures and appends base_path to the current file's path
def create_test_fixtures(*fixture_names)
- ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
+ ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
end
# custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 92e31a3e44..f1362dd15f 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -14,6 +14,10 @@ module ActiveRecord
assert_equal 'id', @connection.primary_key('ex')
end
+ def test_primary_key_works_tables_containing_capital_letters
+ assert_equal 'id', @connection.primary_key('CamelCase')
+ end
+
def test_non_standard_primary_key
@connection.exec_query('drop table if exists ex')
@connection.exec_query('create table ex(data character varying(255) primary key)')
diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
index f8a605b67c..685f0ea74f 100644
--- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require 'ipaddr'
module ActiveRecord
module ConnectionAdapters
@@ -20,6 +21,18 @@ module ActiveRecord
assert_equal 'f', @conn.type_cast(false, c)
end
+ def test_type_cast_cidr
+ ip = IPAddr.new('255.0.0.0/8')
+ c = Column.new(nil, ip, 'cidr')
+ assert_equal ip, @conn.type_cast(ip, c)
+ end
+
+ def test_type_cast_inet
+ ip = IPAddr.new('255.1.0.0/8')
+ c = Column.new(nil, ip, 'inet')
+ assert_equal ip, @conn.type_cast(ip, c)
+ end
+
def test_quote_float_nan
nan = 0.0/0
c = Column.new(nil, 1, 'float')
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 9208f53997..cd31900d4e 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -72,7 +72,7 @@ class SchemaTest < ActiveRecord::TestCase
end
def test_schema_names
- assert_equal ["public", "test_schema", "test_schema2"], @connection.schema_names
+ assert_equal ["public", "schema_1", "test_schema", "test_schema2"], @connection.schema_names
end
def test_create_schema
@@ -97,7 +97,7 @@ class SchemaTest < ActiveRecord::TestCase
def test_drop_schema
begin
- @connection.create_schema "test_schema3"
+ @connection.create_schema "test_schema3"
ensure
@connection.drop_schema "test_schema3"
end
diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
index 7eef4ace81..74288a98d1 100644
--- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
@@ -59,7 +59,7 @@ class CopyTableTest < ActiveRecord::TestCase
def test_copy_table_with_unconventional_primary_key
test_copy_table('owners', 'owners_unconventional') do |from, to, options|
- original_pk = @connection.primary_key('owners')
+ original_pk = @connection.primary_key('owners')
copied_pk = @connection.primary_key('owners_unconventional')
assert_equal original_pk, copied_pk
end
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 f3520d43e0..42f5b69d4e 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
@@ -799,6 +799,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, developer.projects.count
end
+ def test_counting_should_not_fire_sql_if_parent_is_unsaved
+ assert_no_queries do
+ assert_equal 0, Developer.new.projects.count
+ end
+ end
+
unless current_adapter?(:PostgreSQLAdapter)
def test_count_with_finder_sql
assert_equal 3, projects(:active_record).developers_with_finder_sql.count
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 4b56037a08..50c23c863f 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -262,6 +262,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal firm.limited_clients.length, firm.limited_clients.count
end
+ def test_counting_should_not_fire_sql_if_parent_is_unsaved
+ assert_no_queries do
+ assert_equal 0, Person.new.readers.count
+ end
+ end
+
def test_finding
assert_equal 2, Firm.all.merge!(:order => "id").first.clients.length
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index d4ceae6f80..b2a5d9d6f7 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -19,7 +19,6 @@ require 'models/book'
require 'models/subscription'
require 'models/essay'
require 'models/category'
-require 'models/owner'
require 'models/categorization'
require 'models/member'
require 'models/membership'
@@ -767,6 +766,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal 1, authors(:mary).categories.general.count
end
+ def test_counting_should_not_fire_sql_if_parent_is_unsaved
+ assert_no_queries do
+ assert_equal 0, Person.new.posts.count
+ end
+ end
+
def test_has_many_through_belongs_to_should_update_when_the_through_foreign_key_changes
post = posts(:eager_other)
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 86893ec4b3..9b00c21b52 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -231,6 +231,14 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_equal "2", categories(:sti_test).authors_with_select.first.post_id.to_s
end
+ def test_create_through_has_many_with_piggyback
+ category = categories(:sti_test)
+ ernie = category.authors_with_select.create(:name => 'Ernie')
+ assert_nothing_raised do
+ assert_equal ernie, category.authors_with_select.detect {|a| a.name == 'Ernie'}
+ end
+ end
+
def test_include_has_many_through
posts = Post.all.merge!(:order => 'posts.id').to_a
posts_with_authors = Post.all.merge!(:includes => :authors, :order => 'posts.id').to_a
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index d08b157011..c2b58fd7d1 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -542,10 +542,10 @@ class AttributeMethodsTest < ActiveRecord::TestCase
val = t.send attr_name unless attr_name == "type"
if attribute_gets_cached
assert cached_columns.include?(attr_name)
- assert_equal val, cache[attr_name.to_sym]
+ assert_equal val, cache[attr_name]
else
assert uncached_columns.include?(attr_name)
- assert !cache.include?(attr_name.to_sym)
+ assert !cache.include?(attr_name)
end
end
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index fd4f09ab36..16ce150396 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -16,7 +16,6 @@ require 'models/ship_part'
require 'models/tag'
require 'models/tagging'
require 'models/treasure'
-require 'models/company'
require 'models/eye'
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
@@ -145,7 +144,7 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
firm = Firm.first
firm.account = Account.first
- assert_queries(Firm.partial_updates? ? 0 : 1) { firm.save! }
+ assert_queries(Firm.partial_writes? ? 0 : 1) { firm.save! }
firm = Firm.first.dup
firm.account = Account.first
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index fbfdd0f07a..0f859bf452 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -617,343 +617,6 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal 'value2', weird.read_attribute('a$b')
end
- def test_multiparameter_attributes_on_date
- attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
- end
-
- def test_multiparameter_attributes_on_date_with_empty_year
- attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_date_with_empty_month
- attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_date_with_empty_day
- attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_date_with_empty_day_and_year
- attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_date_with_empty_day_and_month
- attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_date_with_empty_year_and_month
- attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_date_with_all_empty
- attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_time
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
- end
-
- def test_multiparameter_attributes_on_time_with_no_date
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- attributes = {
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- end
- assert_equal("written_on", ex.errors[0].attribute)
- end
-
- def test_multiparameter_attributes_on_time_with_invalid_time_params
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "2004", "written_on(5i)" => "36", "written_on(6i)" => "64",
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- end
- assert_equal("written_on", ex.errors[0].attribute)
- end
-
- def test_multiparameter_attributes_on_time_with_old_date
- attributes = {
- "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
- assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
- end
-
- def test_multiparameter_attributes_on_time_will_raise_on_big_time_if_missing_date_parts
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- attributes = {
- "written_on(4i)" => "16", "written_on(5i)" => "24"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- end
- assert_equal("written_on", ex.errors[0].attribute)
- end
-
- def test_multiparameter_attributes_on_time_with_raise_on_small_time_if_missing_date_parts
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- attributes = {
- "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- end
- assert_equal("written_on", ex.errors[0].attribute)
- end
-
- def test_multiparameter_attributes_on_time_will_ignore_hour_if_missing
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "12", "written_on(3i)" => "12",
- "written_on(5i)" => "12", "written_on(6i)" => "02"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.local(2004, 12, 12, 0, 12, 2), topic.written_on
- end
-
- def test_multiparameter_attributes_on_time_will_ignore_hour_if_blank
- attributes = {
- "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
- "written_on(4i)" => "", "written_on(5i)" => "12", "written_on(6i)" => "02"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_nil topic.written_on
- end
-
- def test_multiparameter_attributes_on_time_will_ignore_date_if_empty
- attributes = {
- "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
- "written_on(4i)" => "16", "written_on(5i)" => "24"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_nil topic.written_on
- end
- def test_multiparameter_attributes_on_time_with_seconds_will_ignore_date_if_empty
- attributes = {
- "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
- "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_nil topic.written_on
- end
-
- def test_multiparameter_attributes_on_time_with_utc
- ActiveRecord::Base.default_timezone = :utc
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
- end
-
- def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
- ActiveRecord::Base.time_zone_aware_attributes = true
- ActiveRecord::Base.default_timezone = :utc
- Time.zone = ActiveSupport::TimeZone[-28800]
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
- assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
- assert_equal Time.zone, topic.written_on.time_zone
- end
-
- def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
- Time.zone = ActiveSupport::TimeZone[-28800]
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
- assert_equal false, topic.written_on.respond_to?(:time_zone)
- end
-
- def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
- ActiveRecord::Base.time_zone_aware_attributes = true
- ActiveRecord::Base.default_timezone = :utc
- Time.zone = ActiveSupport::TimeZone[-28800]
- Topic.skip_time_zone_conversion_for_attributes = [:written_on]
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
- assert_equal false, topic.written_on.respond_to?(:time_zone)
- ensure
- Topic.skip_time_zone_conversion_for_attributes = []
- end
-
- # Oracle, and Sybase do not have a TIME datatype.
- unless current_adapter?(:OracleAdapter, :SybaseAdapter)
- def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
- ActiveRecord::Base.time_zone_aware_attributes = true
- ActiveRecord::Base.default_timezone = :utc
- Time.zone = ActiveSupport::TimeZone[-28800]
- attributes = {
- "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1",
- "bonus_time(4i)" => "16", "bonus_time(5i)" => "24"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
- assert topic.bonus_time.utc?
- end
- end
-
- def test_multiparameter_attributes_on_time_with_empty_seconds
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
- end
-
- def test_multiparameter_attributes_setting_time_attribute
- return skip "Oracle does not have TIME data type" if current_adapter? :OracleAdapter
-
- topic = Topic.new( "bonus_time(4i)"=> "01", "bonus_time(5i)" => "05" )
- assert_equal 1, topic.bonus_time.hour
- assert_equal 5, topic.bonus_time.min
- end
-
- def test_multiparameter_attributes_setting_date_attribute
- topic = Topic.new( "written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11" )
- assert_equal 1952, topic.written_on.year
- assert_equal 3, topic.written_on.month
- assert_equal 11, topic.written_on.day
- end
-
- def test_multiparameter_attributes_setting_date_and_time_attribute
- topic = Topic.new(
- "written_on(1i)" => "1952",
- "written_on(2i)" => "3",
- "written_on(3i)" => "11",
- "written_on(4i)" => "13",
- "written_on(5i)" => "55")
- assert_equal 1952, topic.written_on.year
- assert_equal 3, topic.written_on.month
- assert_equal 11, topic.written_on.day
- assert_equal 13, topic.written_on.hour
- assert_equal 55, topic.written_on.min
- end
-
- def test_multiparameter_attributes_setting_time_but_not_date_on_date_field
- assert_raise( ActiveRecord::MultiparameterAssignmentErrors ) do
- Topic.new( "written_on(4i)" => "13", "written_on(5i)" => "55" )
- end
- end
-
- def test_multiparameter_assignment_of_aggregation
- customer = Customer.new
- address = Address.new("The Street", "The City", "The Country")
- attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
- customer.attributes = attributes
- assert_equal address, customer.address
- end
-
- def test_multiparameter_assignment_of_aggregation_out_of_order
- customer = Customer.new
- address = Address.new("The Street", "The City", "The Country")
- attributes = { "address(3)" => address.country, "address(2)" => address.city, "address(1)" => address.street }
- customer.attributes = attributes
- assert_equal address, customer.address
- end
-
- def test_multiparameter_assignment_of_aggregation_with_missing_values
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- customer = Customer.new
- address = Address.new("The Street", "The City", "The Country")
- attributes = { "address(2)" => address.city, "address(3)" => address.country }
- customer.attributes = attributes
- end
- assert_equal("address", ex.errors[0].attribute)
- end
-
- def test_multiparameter_assignment_of_aggregation_with_blank_values
- customer = Customer.new
- address = Address.new("The Street", "The City", "The Country")
- attributes = { "address(1)" => "", "address(2)" => address.city, "address(3)" => address.country }
- customer.attributes = attributes
- assert_equal Address.new(nil, "The City", "The Country"), customer.address
- end
-
- def test_multiparameter_assignment_of_aggregation_with_large_index
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- customer = Customer.new
- address = Address.new("The Street", "The City", "The Country")
- attributes = { "address(1)" => "The Street", "address(2)" => address.city, "address(3000)" => address.country }
- customer.attributes = attributes
- end
-
- assert_equal("address", ex.errors[0].attribute)
- end
-
def test_attributes_on_dummy_time
# Oracle, and Sybase do not have a TIME datatype.
return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 6cb6c469d2..abbf2a765e 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -6,6 +6,8 @@ require 'models/edge'
require 'models/organization'
require 'models/possession'
require 'models/topic'
+require 'models/minivan'
+require 'models/speedometer'
Company.has_many :accounts
@@ -239,21 +241,12 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_group_by_association_with_non_numeric_foreign_key
- ActiveRecord::Base.connection.expects(:select_all).returns([{"count_all" => 1, "firm_id" => "ABC"}])
+ Speedometer.create! id: 'ABC'
+ Minivan.create! id: 'OMG', speedometer_id: 'ABC'
- firm = mock()
- firm.expects(:id).returns("ABC")
- firm.expects(:class).returns(Firm)
- Company.expects(:find).with(["ABC"]).returns([firm])
-
- column = mock()
- column.expects(:name).at_least_once.returns(:firm_id)
- column.expects(:type_cast).with("ABC").returns("ABC")
- Account.expects(:columns).at_least_once.returns([column])
-
- c = Account.group(:firm).count(:all)
+ c = Minivan.group(:speedometer).count(:all)
first_key = c.keys.first
- assert_equal Firm, first_key.class
+ assert_equal Speedometer, first_key.class
assert_equal 1, c[first_key]
end
@@ -378,6 +371,10 @@ class CalculationsTest < ActiveRecord::TestCase
end
end
+ def test_sum_expression_returns_zero_when_no_records_to_sum
+ assert_equal 0, Account.where('1 = 2').sum("2 * credit_limit")
+ end
+
def test_count_with_from_option
assert_equal Company.count(:all), Company.from('companies').count(:all)
assert_equal Account.where("credit_limit = 50").count(:all),
diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb
index ee443741ca..fc46a249c8 100644
--- a/activerecord/test/cases/counter_cache_test.rb
+++ b/activerecord/test/cases/counter_cache_test.rb
@@ -10,9 +10,12 @@ require 'models/dog'
require 'models/dog_lover'
require 'models/person'
require 'models/friendship'
+require 'models/subscriber'
+require 'models/subscription'
+require 'models/book'
class CounterCacheTest < ActiveRecord::TestCase
- fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers, :people, :friendships
+ fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers, :people, :friendships, :subscribers, :subscriptions, :books
class ::SpecialTopic < ::Topic
has_many :special_replies, :foreign_key => 'parent_id'
@@ -118,4 +121,14 @@ class CounterCacheTest < ActiveRecord::TestCase
Person.reset_counters(michael.id, :followers)
end
end
+
+ test "reset counter of has_many :through association" do
+ subscriber = subscribers('second')
+ Subscriber.reset_counters(subscriber.id, 'books')
+ Subscriber.increment_counter('books_count', subscriber.id)
+
+ assert_difference 'subscriber.reload.books_count', -1 do
+ Subscriber.reset_counters(subscriber.id, 'books')
+ end
+ end
end
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index deaf5252db..0df872ff10 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -51,11 +51,60 @@ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
# We don't want that to happen, so we disable transactional fixtures here.
self.use_transactional_fixtures = false
- # MySQL 5 and higher is quirky with not null text/blob columns.
- # With MySQL Text/blob columns cannot have defaults. If the column is not
- # null MySQL will report that the column has a null default
- # but it behaves as though the column had a default of ''
- def test_mysql_text_not_null_defaults
+ def using_strict(strict)
+ connection = ActiveRecord::Model.remove_connection
+ ActiveRecord::Model.establish_connection connection.merge(strict: strict)
+ yield
+ ensure
+ ActiveRecord::Model.remove_connection
+ ActiveRecord::Model.establish_connection connection
+ end
+
+ # MySQL cannot have defaults on text/blob columns. It reports the
+ # default value as null.
+ #
+ # Despite this, in non-strict mode, MySQL will use an empty string
+ # as the default value of the field, if no other value is
+ # specified.
+ #
+ # Therefore, in non-strict mode, we want column.default to report
+ # an empty string as its default, to be consistent with that.
+ #
+ # In strict mode, column.default should be nil.
+ def test_mysql_text_not_null_defaults_non_strict
+ using_strict(false) do
+ with_text_blob_not_null_table do |klass|
+ assert_equal '', klass.columns_hash['non_null_blob'].default
+ assert_equal '', klass.columns_hash['non_null_text'].default
+
+ assert_nil klass.columns_hash['null_blob'].default
+ assert_nil klass.columns_hash['null_text'].default
+
+ instance = klass.create!
+
+ assert_equal '', instance.non_null_text
+ assert_equal '', instance.non_null_blob
+
+ assert_nil instance.null_text
+ assert_nil instance.null_blob
+ end
+ end
+ end
+
+ def test_mysql_text_not_null_defaults_strict
+ using_strict(true) do
+ with_text_blob_not_null_table do |klass|
+ assert_nil klass.columns_hash['non_null_blob'].default
+ assert_nil klass.columns_hash['non_null_text'].default
+ assert_nil klass.columns_hash['null_blob'].default
+ assert_nil klass.columns_hash['null_text'].default
+
+ assert_raises(ActiveRecord::StatementInvalid) { klass.create }
+ end
+ end
+ end
+
+ def with_text_blob_not_null_table
klass = Class.new(ActiveRecord::Base)
klass.table_name = 'test_mysql_text_not_null_defaults'
klass.connection.create_table klass.table_name do |t|
@@ -64,19 +113,8 @@ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
t.column :null_text, :text, :null => true
t.column :null_blob, :blob, :null => true
end
- assert_equal '', klass.columns_hash['non_null_blob'].default
- assert_equal '', klass.columns_hash['non_null_text'].default
-
- assert_nil klass.columns_hash['null_blob'].default
- assert_nil klass.columns_hash['null_text'].default
- assert_nothing_raised do
- instance = klass.create!
- assert_equal '', instance.non_null_text
- assert_equal '', instance.non_null_blob
- assert_nil instance.null_text
- assert_nil instance.null_blob
- end
+ yield klass
ensure
klass.connection.drop_table(klass.table_name) rescue nil
end
@@ -109,3 +147,43 @@ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
end
end
end
+
+if current_adapter?(:PostgreSQLAdapter)
+ class DefaultsUsingMultipleSchemasAndDomainTest < ActiveSupport::TestCase
+ def setup
+ @connection = ActiveRecord::Base.connection
+
+ @old_search_path = @connection.schema_search_path
+ @connection.schema_search_path = "schema_1, pg_catalog"
+ @connection.create_table "defaults" do |t|
+ t.text "text_col", :default => "some value"
+ t.string "string_col", :default => "some value"
+ end
+ Default.reset_column_information
+ end
+
+ def test_text_defaults_in_new_schema_when_overriding_domain
+ assert_equal "some value", Default.new.text_col, "Default of text column was not correctly parse"
+ end
+
+ def test_string_defaults_in_new_schema_when_overriding_domain
+ assert_equal "some value", Default.new.string_col, "Default of string column was not correctly parse"
+ end
+
+ def test_bpchar_defaults_in_new_schema_when_overriding_domain
+ @connection.execute "ALTER TABLE defaults ADD bpchar_col bpchar DEFAULT 'some value'"
+ Default.reset_column_information
+ assert_equal "some value", Default.new.bpchar_col, "Default of bpchar column was not correctly parse"
+ end
+
+ def test_text_defaults_after_updating_column_default
+ @connection.execute "ALTER TABLE defaults ALTER COLUMN text_col SET DEFAULT 'some text'::schema_1.text"
+ assert_equal "some text", Default.new.text_col, "Default of text column was not correctly parse after updating default using '::text' since postgreSQL will add parens to the default in db"
+ end
+
+ def teardown
+ @connection.schema_search_path = @old_search_path
+ Default.reset_column_information
+ end
+ end
+end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 7334514f9a..40f1dbccde 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -311,12 +311,12 @@ class DirtyTest < ActiveRecord::TestCase
pirate = Pirate.new(:catchphrase => 'foo')
old_updated_on = 1.hour.ago.beginning_of_day
- with_partial_updates Pirate, false do
+ with_partial_writes Pirate, false do
assert_queries(2) { 2.times { pirate.save! } }
Pirate.where(id: pirate.id).update_all(:updated_on => old_updated_on)
end
- with_partial_updates Pirate, true do
+ with_partial_writes Pirate, true do
assert_queries(0) { 2.times { pirate.save! } }
assert_equal old_updated_on, pirate.reload.updated_on
@@ -329,12 +329,12 @@ class DirtyTest < ActiveRecord::TestCase
person = Person.new(:first_name => 'foo')
old_lock_version = 1
- with_partial_updates Person, false do
+ with_partial_writes Person, false do
assert_queries(2) { 2.times { person.save! } }
Person.where(id: person.id).update_all(:first_name => 'baz')
end
- with_partial_updates Person, true do
+ with_partial_writes Person, true do
assert_queries(0) { 2.times { person.save! } }
assert_equal old_lock_version, person.reload.lock_version
@@ -408,8 +408,8 @@ class DirtyTest < ActiveRecord::TestCase
assert !pirate.catchphrase_changed?
end
- def test_save_should_store_serialized_attributes_even_with_partial_updates
- with_partial_updates(Topic) do
+ def test_save_should_store_serialized_attributes_even_with_partial_writes
+ with_partial_writes(Topic) do
topic = Topic.create!(:content => {:a => "a"})
topic.content[:b] = "b"
#assert topic.changed? # Known bug, will fail
@@ -421,7 +421,7 @@ class DirtyTest < ActiveRecord::TestCase
end
def test_save_always_should_update_timestamps_when_serialized_attributes_are_present
- with_partial_updates(Topic) do
+ with_partial_writes(Topic) do
topic = Topic.create!(:content => {:a => "a"})
topic.save!
@@ -434,8 +434,8 @@ class DirtyTest < ActiveRecord::TestCase
end
end
- def test_save_should_not_save_serialized_attribute_with_partial_updates_if_not_present
- with_partial_updates(Topic) do
+ def test_save_should_not_save_serialized_attribute_with_partial_writes_if_not_present
+ with_partial_writes(Topic) do
Topic.create!(:author_name => 'Bill', :content => {:a => "a"})
topic = Topic.select('id, author_name').first
topic.update_columns author_name: 'John'
@@ -552,7 +552,7 @@ class DirtyTest < ActiveRecord::TestCase
end
test "partial insert" do
- with_partial_updates Person do
+ with_partial_writes Person do
jon = nil
assert_sql(/first_name/i) do
jon = Person.create! first_name: 'Jon'
@@ -568,20 +568,34 @@ class DirtyTest < ActiveRecord::TestCase
end
test "partial insert with empty values" do
- with_partial_updates Aircraft do
+ with_partial_writes Aircraft do
a = Aircraft.create!
a.reload
assert_not_nil a.id
end
end
+ test "partial_updates config attribute is deprecated" do
+ klass = Class.new(ActiveRecord::Base)
+
+ assert klass.partial_writes?
+ assert_deprecated { assert klass.partial_updates? }
+ assert_deprecated { assert klass.partial_updates }
+
+ assert_deprecated { klass.partial_updates = false }
+
+ assert !klass.partial_writes?
+ assert_deprecated { assert !klass.partial_updates? }
+ assert_deprecated { assert !klass.partial_updates }
+ end
+
private
- def with_partial_updates(klass, on = true)
- old = klass.partial_updates?
- klass.partial_updates = on
+ def with_partial_writes(klass, on = true)
+ old = klass.partial_writes?
+ klass.partial_writes = on
yield
ensure
- klass.partial_updates = old
+ klass.partial_writes = old
end
def check_pirate_after_save_failure(pirate)
diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb
index 71b2b16608..4e2adff344 100644
--- a/activerecord/test/cases/dup_test.rb
+++ b/activerecord/test/cases/dup_test.rb
@@ -107,5 +107,19 @@ module ActiveRecord
assert Topic.after_initialize_called
end
+ def test_dup_validity_is_independent
+ Topic.validates_presence_of :title
+ topic = Topic.new("title" => "Litterature")
+ topic.valid?
+
+ duped = topic.dup
+ duped.title = nil
+ assert duped.invalid?
+
+ topic.title = nil
+ duped.title = 'Mathematics'
+ assert topic.invalid?
+ assert duped.valid?
+ end
end
end
diff --git a/activerecord/test/cases/fixtures/file_test.rb b/activerecord/test/cases/fixture_set/file_test.rb
index e623fbe4d1..a029fedbd3 100644
--- a/activerecord/test/cases/fixtures/file_test.rb
+++ b/activerecord/test/cases/fixture_set/file_test.rb
@@ -2,7 +2,7 @@ require 'cases/helper'
require 'tempfile'
module ActiveRecord
- class Fixtures
+ class FixtureSet
class FileTest < ActiveRecord::TestCase
def test_open
fh = File.open(::File.join(FIXTURES_ROOT, "accounts.yml"))
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index c28f8de682..b0b29f5f42 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -40,7 +40,7 @@ class FixturesTest < ActiveRecord::TestCase
FIXTURES.each do |name|
fixtures = nil
assert_nothing_raised { fixtures = create_fixtures(name).first }
- assert_kind_of(ActiveRecord::Fixtures, fixtures)
+ assert_kind_of(ActiveRecord::FixtureSet, fixtures)
fixtures.each { |_name, fixture|
fixture.each { |key, value|
assert_match(MATCH_ATTRIBUTE_NAME, key)
@@ -57,7 +57,7 @@ class FixturesTest < ActiveRecord::TestCase
dir = File.dirname badyaml.path
name = File.basename badyaml.path, '.yml'
assert_raises(ActiveRecord::Fixture::FormatError) do
- ActiveRecord::Fixtures.create_fixtures(dir, name)
+ ActiveRecord::FixtureSet.create_fixtures(dir, name)
end
ensure
badyaml.close
@@ -65,7 +65,7 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_create_fixtures
- fixtures = ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, "parrots")
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, "parrots")
assert Parrot.find_by_name('Curious George'), 'George is not in the database'
assert fixtures.detect { |f| f.name == 'parrots' }, "no fixtures named 'parrots' in #{fixtures.map(&:name).inspect}"
end
@@ -74,11 +74,11 @@ class FixturesTest < ActiveRecord::TestCase
fixtures_array = nil
assert_nothing_raised { fixtures_array = create_fixtures(*FIXTURES) }
assert_kind_of(Array, fixtures_array)
- fixtures_array.each { |fixtures| assert_kind_of(ActiveRecord::Fixtures, fixtures) }
+ fixtures_array.each { |fixtures| assert_kind_of(ActiveRecord::FixtureSet, fixtures) }
end
def test_create_symbol_fixtures
- fixtures = ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, :collections, :collections => Course) { Course.connection }
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, :collections, :collections => Course) { Course.connection }
assert Course.find_by_name('Collection'), 'course is not in the database'
assert fixtures.detect { |f| f.name == 'collections' }, "no fixtures named 'collections' in #{fixtures.map(&:name).inspect}"
@@ -102,7 +102,7 @@ class FixturesTest < ActiveRecord::TestCase
if ActiveRecord::Base.connection.supports_migrations?
def test_inserts_with_pre_and_suffix
# Reset cache to make finds on the new table work
- ActiveRecord::Fixtures.reset_cache
+ ActiveRecord::FixtureSet.reset_cache
ActiveRecord::Base.connection.create_table :prefix_other_topics_suffix do |t|
t.column :title, :string
@@ -190,11 +190,11 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_empty_yaml_fixture
- assert_not_nil ActiveRecord::Fixtures.new( Account.connection, "accounts", 'Account', FIXTURES_ROOT + "/naked/yml/accounts")
+ assert_not_nil ActiveRecord::FixtureSet.new( Account.connection, "accounts", 'Account', FIXTURES_ROOT + "/naked/yml/accounts")
end
def test_empty_yaml_fixture_with_a_comment_in_it
- assert_not_nil ActiveRecord::Fixtures.new( Account.connection, "companies", 'Company', FIXTURES_ROOT + "/naked/yml/companies")
+ assert_not_nil ActiveRecord::FixtureSet.new( Account.connection, "companies", 'Company', FIXTURES_ROOT + "/naked/yml/companies")
end
def test_nonexistent_fixture_file
@@ -204,19 +204,19 @@ class FixturesTest < ActiveRecord::TestCase
assert Dir[nonexistent_fixture_path+"*"].empty?
assert_raise(Errno::ENOENT) do
- ActiveRecord::Fixtures.new( Account.connection, "companies", 'Company', nonexistent_fixture_path)
+ ActiveRecord::FixtureSet.new( Account.connection, "companies", 'Company', nonexistent_fixture_path)
end
end
def test_dirty_dirty_yaml_file
assert_raise(ActiveRecord::Fixture::FormatError) do
- ActiveRecord::Fixtures.new( Account.connection, "courses", 'Course', FIXTURES_ROOT + "/naked/yml/courses")
+ ActiveRecord::FixtureSet.new( Account.connection, "courses", 'Course', FIXTURES_ROOT + "/naked/yml/courses")
end
end
def test_omap_fixtures
assert_nothing_raised do
- fixtures = ActiveRecord::Fixtures.new(Account.connection, 'categories', 'Category', FIXTURES_ROOT + "/categories_ordered")
+ fixtures = ActiveRecord::FixtureSet.new(Account.connection, 'categories', 'Category', FIXTURES_ROOT + "/categories_ordered")
fixtures.each.with_index do |(name, fixture), i|
assert_equal "fixture_no_#{i}", name
@@ -254,7 +254,7 @@ if Account.connection.respond_to?(:reset_pk_sequence!)
def setup
@instances = [Account.new(:credit_limit => 50), Company.new(:name => 'RoR Consulting')]
- ActiveRecord::Fixtures.reset_cache # make sure tables get reinitialized
+ ActiveRecord::FixtureSet.reset_cache # make sure tables get reinitialized
end
def test_resets_to_min_pk_with_specified_pk_and_sequence
@@ -582,13 +582,13 @@ class FasterFixturesTest < ActiveRecord::TestCase
def load_extra_fixture(name)
fixture = create_fixtures(name).first
- assert fixture.is_a?(ActiveRecord::Fixtures)
+ assert fixture.is_a?(ActiveRecord::FixtureSet)
@loaded_fixtures[fixture.table_name] = fixture
end
def test_cache
- assert ActiveRecord::Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'categories')
- assert ActiveRecord::Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'authors')
+ assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, 'categories')
+ assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, 'authors')
assert_no_queries do
create_fixtures('categories')
@@ -596,7 +596,7 @@ class FasterFixturesTest < ActiveRecord::TestCase
end
load_extra_fixture('posts')
- assert ActiveRecord::Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'posts')
+ assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, 'posts')
self.class.setup_fixture_accessors :posts
assert_equal 'Welcome to the weblog', posts(:welcome).title
end
@@ -606,17 +606,17 @@ class FoxyFixturesTest < ActiveRecord::TestCase
fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers, :developers, :"admin/accounts", :"admin/users"
def test_identifies_strings
- assert_equal(ActiveRecord::Fixtures.identify("foo"), ActiveRecord::Fixtures.identify("foo"))
- assert_not_equal(ActiveRecord::Fixtures.identify("foo"), ActiveRecord::Fixtures.identify("FOO"))
+ assert_equal(ActiveRecord::FixtureSet.identify("foo"), ActiveRecord::FixtureSet.identify("foo"))
+ assert_not_equal(ActiveRecord::FixtureSet.identify("foo"), ActiveRecord::FixtureSet.identify("FOO"))
end
def test_identifies_symbols
- assert_equal(ActiveRecord::Fixtures.identify(:foo), ActiveRecord::Fixtures.identify(:foo))
+ assert_equal(ActiveRecord::FixtureSet.identify(:foo), ActiveRecord::FixtureSet.identify(:foo))
end
def test_identifies_consistently
- assert_equal 207281424, ActiveRecord::Fixtures.identify(:ruby)
- assert_equal 1066363776, ActiveRecord::Fixtures.identify(:sapphire_2)
+ assert_equal 207281424, ActiveRecord::FixtureSet.identify(:ruby)
+ assert_equal 1066363776, ActiveRecord::FixtureSet.identify(:sapphire_2)
end
TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on)
@@ -757,7 +757,7 @@ class FixtureLoadingTest < ActiveRecord::TestCase
end
class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase
- ActiveRecord::Fixtures.reset_cache
+ ActiveRecord::FixtureSet.reset_cache
set_fixture_class :randomly_named_a9 =>
ClassNameThatDoesNotFollowCONVENTIONS,
@@ -782,7 +782,7 @@ class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase
end
def test_table_name_is_defined_in_the_model
- assert_equal 'randomly_named_table', ActiveRecord::Fixtures::all_loaded_fixtures["admin/randomly_named_a9"].table_name
+ assert_equal 'randomly_named_table', ActiveRecord::FixtureSet::all_loaded_fixtures["admin/randomly_named_a9"].table_name
assert_equal 'randomly_named_table', Admin::ClassNameThatDoesNotFollowCONVENTIONS.table_name
end
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index f39111ba77..cff6689c15 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -80,7 +80,7 @@ class ActiveSupport::TestCase
self.use_transactional_fixtures = true
def create_fixtures(*fixture_set_names, &block)
- ActiveRecord::Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, fixture_class_names, &block)
+ ActiveRecord::FixtureSet.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, fixture_class_names, &block)
end
end
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index ec4c554abb..17c1634444 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -293,7 +293,7 @@ module ActiveRecord
connection.create_table :testings do |t|
t.column :foo, :string, limit: 100
t.column :bar, :decimal, precision: 8, scale: 2
- t.column :taggable_id, :integer, null: false
+ t.column :taggable_id, :integer, null: false
t.column :taggable_type, :string, default: 'Photo'
end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 3c0d2b18d9..c155f29973 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -344,11 +344,7 @@ class MigrationTest < ActiveRecord::TestCase
columns = Person.connection.columns(:binary_testings)
data_column = columns.detect { |c| c.name == "data" }
- if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
- assert_equal '', data_column.default
- else
- assert_nil data_column.default
- end
+ assert_nil data_column.default
Person.connection.drop_table :binary_testings rescue nil
end
diff --git a/activerecord/test/cases/multiparameter_attributes_test.rb b/activerecord/test/cases/multiparameter_attributes_test.rb
new file mode 100644
index 0000000000..1209f5460f
--- /dev/null
+++ b/activerecord/test/cases/multiparameter_attributes_test.rb
@@ -0,0 +1,350 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/customer'
+
+class MultiParameterAttributeTest < ActiveRecord::TestCase
+ fixtures :topics
+
+ def setup
+ ActiveRecord::Base.time_zone_aware_attributes = false
+ ActiveRecord::Base.default_timezone = :local
+ Time.zone = nil
+ end
+
+ def test_multiparameter_attributes_on_date
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
+ end
+
+ def test_multiparameter_attributes_on_date_with_empty_year
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_date_with_empty_month
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_date_with_empty_day
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_date_with_empty_day_and_year
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_date_with_empty_day_and_month
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_date_with_empty_year_and_month
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_date_with_all_empty
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_time
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
+ end
+
+ def test_multiparameter_attributes_on_time_with_no_date
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ attributes = {
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ end
+ assert_equal("written_on", ex.errors[0].attribute)
+ end
+
+ def test_multiparameter_attributes_on_time_with_invalid_time_params
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "2004", "written_on(5i)" => "36", "written_on(6i)" => "64",
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ end
+ assert_equal("written_on", ex.errors[0].attribute)
+ end
+
+ def test_multiparameter_attributes_on_time_with_old_date
+ attributes = {
+ "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
+ assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
+ end
+
+ def test_multiparameter_attributes_on_time_will_raise_on_big_time_if_missing_date_parts
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ attributes = {
+ "written_on(4i)" => "16", "written_on(5i)" => "24"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ end
+ assert_equal("written_on", ex.errors[0].attribute)
+ end
+
+ def test_multiparameter_attributes_on_time_with_raise_on_small_time_if_missing_date_parts
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ attributes = {
+ "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ end
+ assert_equal("written_on", ex.errors[0].attribute)
+ end
+
+ def test_multiparameter_attributes_on_time_will_ignore_hour_if_missing
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "12", "written_on(3i)" => "12",
+ "written_on(5i)" => "12", "written_on(6i)" => "02"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.local(2004, 12, 12, 0, 12, 2), topic.written_on
+ end
+
+ def test_multiparameter_attributes_on_time_will_ignore_hour_if_blank
+ attributes = {
+ "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
+ "written_on(4i)" => "", "written_on(5i)" => "12", "written_on(6i)" => "02"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_nil topic.written_on
+ end
+
+ def test_multiparameter_attributes_on_time_will_ignore_date_if_empty
+ attributes = {
+ "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
+ "written_on(4i)" => "16", "written_on(5i)" => "24"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_nil topic.written_on
+ end
+ def test_multiparameter_attributes_on_time_with_seconds_will_ignore_date_if_empty
+ attributes = {
+ "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
+ "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_nil topic.written_on
+ end
+
+ def test_multiparameter_attributes_on_time_with_utc
+ ActiveRecord::Base.default_timezone = :utc
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
+ end
+
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
+ ActiveRecord::Base.time_zone_aware_attributes = true
+ ActiveRecord::Base.default_timezone = :utc
+ Time.zone = ActiveSupport::TimeZone[-28800]
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
+ assert_equal Time.zone, topic.written_on.time_zone
+ end
+
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
+ Time.zone = ActiveSupport::TimeZone[-28800]
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
+ end
+
+ def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
+ ActiveRecord::Base.time_zone_aware_attributes = true
+ ActiveRecord::Base.default_timezone = :utc
+ Time.zone = ActiveSupport::TimeZone[-28800]
+ Topic.skip_time_zone_conversion_for_attributes = [:written_on]
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
+ ensure
+ Topic.skip_time_zone_conversion_for_attributes = []
+ end
+
+ # Oracle, and Sybase do not have a TIME datatype.
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter)
+ def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
+ ActiveRecord::Base.time_zone_aware_attributes = true
+ ActiveRecord::Base.default_timezone = :utc
+ Time.zone = ActiveSupport::TimeZone[-28800]
+ attributes = {
+ "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1",
+ "bonus_time(4i)" => "16", "bonus_time(5i)" => "24"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
+ assert topic.bonus_time.utc?
+ end
+ end
+
+ def test_multiparameter_attributes_on_time_with_empty_seconds
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
+ end
+
+ def test_multiparameter_attributes_setting_time_attribute
+ return skip "Oracle does not have TIME data type" if current_adapter? :OracleAdapter
+
+ topic = Topic.new( "bonus_time(4i)"=> "01", "bonus_time(5i)" => "05" )
+ assert_equal 1, topic.bonus_time.hour
+ assert_equal 5, topic.bonus_time.min
+ end
+
+ def test_multiparameter_attributes_setting_date_attribute
+ topic = Topic.new( "written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11" )
+ assert_equal 1952, topic.written_on.year
+ assert_equal 3, topic.written_on.month
+ assert_equal 11, topic.written_on.day
+ end
+
+ def test_multiparameter_attributes_setting_date_and_time_attribute
+ topic = Topic.new(
+ "written_on(1i)" => "1952",
+ "written_on(2i)" => "3",
+ "written_on(3i)" => "11",
+ "written_on(4i)" => "13",
+ "written_on(5i)" => "55")
+ assert_equal 1952, topic.written_on.year
+ assert_equal 3, topic.written_on.month
+ assert_equal 11, topic.written_on.day
+ assert_equal 13, topic.written_on.hour
+ assert_equal 55, topic.written_on.min
+ end
+
+ def test_multiparameter_attributes_setting_time_but_not_date_on_date_field
+ assert_raise( ActiveRecord::MultiparameterAssignmentErrors ) do
+ Topic.new( "written_on(4i)" => "13", "written_on(5i)" => "55" )
+ end
+ end
+
+ def test_multiparameter_assignment_of_aggregation
+ customer = Customer.new
+ address = Address.new("The Street", "The City", "The Country")
+ attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
+ customer.attributes = attributes
+ assert_equal address, customer.address
+ end
+
+ def test_multiparameter_assignment_of_aggregation_out_of_order
+ customer = Customer.new
+ address = Address.new("The Street", "The City", "The Country")
+ attributes = { "address(3)" => address.country, "address(2)" => address.city, "address(1)" => address.street }
+ customer.attributes = attributes
+ assert_equal address, customer.address
+ end
+
+ def test_multiparameter_assignment_of_aggregation_with_missing_values
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ customer = Customer.new
+ address = Address.new("The Street", "The City", "The Country")
+ attributes = { "address(2)" => address.city, "address(3)" => address.country }
+ customer.attributes = attributes
+ end
+ assert_equal("address", ex.errors[0].attribute)
+ end
+
+ def test_multiparameter_assignment_of_aggregation_with_blank_values
+ customer = Customer.new
+ address = Address.new("The Street", "The City", "The Country")
+ attributes = { "address(1)" => "", "address(2)" => address.city, "address(3)" => address.country }
+ customer.attributes = attributes
+ assert_equal Address.new(nil, "The City", "The Country"), customer.address
+ end
+
+ def test_multiparameter_assignment_of_aggregation_with_large_index
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ customer = Customer.new
+ address = Address.new("The Street", "The City", "The Country")
+ attributes = { "address(1)" => "The Street", "address(2)" => address.city, "address(3000)" => address.country }
+ customer.attributes = attributes
+ end
+
+ assert_equal("address", ex.errors[0].attribute)
+ end
+end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 9120083eca..fe9eddbdec 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -156,7 +156,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
end
def test_reject_if_with_blank_nested_attributes_id
- # When using a select list to choose an existing 'ship' id, with :include_blank => true
+ # When using a select list to choose an existing 'ship' id, with include_blank: true
Pirate.accepts_nested_attributes_for :ship, :reject_if => proc {|attributes| attributes[:id].blank? }
pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 2d778e9e90..51a285a2b4 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -48,7 +48,7 @@ class QueryCacheTest < ActiveRecord::TestCase
}
assert_raises(RuntimeError) { mw.call({}) }
- assert_equal connection_id, ActiveRecord::Base.connection_id
+ assert_equal connection_id, ActiveRecord::Base.connection_id
end
def test_middleware_delegates
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 588da68ec1..a9d46f4fba 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -3,7 +3,6 @@ require 'models/topic'
require 'models/customer'
require 'models/company'
require 'models/company_in_module'
-require 'models/subscriber'
require 'models/ship'
require 'models/pirate'
require 'models/price_estimate'
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index b91423351e..5f96145b47 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -5,7 +5,6 @@ require 'models/post'
require 'models/topic'
require 'models/comment'
require 'models/author'
-require 'models/comment'
require 'models/entrant'
require 'models/developer'
require 'models/reply'
@@ -158,6 +157,22 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 4, topics.to_a.size
assert_equal topics(:first).title, topics.first.title
end
+
+ def test_finding_with_assoc_order
+ topics = Topic.order(:id => :desc)
+ assert_equal 4, topics.to_a.size
+ assert_equal topics(:fourth).title, topics.first.title
+ end
+
+ def test_finding_with_reverted_assoc_order
+ topics = Topic.order(:id => :asc).reverse_order
+ assert_equal 4, topics.to_a.size
+ assert_equal topics(:fourth).title, topics.first.title
+ end
+
+ def test_raising_exception_on_invalid_hash_params
+ assert_raise(ArgumentError) { Topic.order(:name, "id DESC", :id => :DeSc) }
+ end
def test_finding_last_with_arel_order
topics = Topic.order(Topic.arel_table[:id].asc)
@@ -1043,6 +1058,39 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 'parrot', parrot.name
end
+ def test_find_or_create_by
+ assert_nil Bird.find_by(name: 'bob')
+
+ bird = Bird.find_or_create_by(name: 'bob')
+ assert bird.persisted?
+
+ assert_equal bird, Bird.find_or_create_by(name: 'bob')
+ end
+
+ def test_find_or_create_by_with_create_with
+ assert_nil Bird.find_by(name: 'bob')
+
+ bird = Bird.create_with(color: 'green').find_or_create_by(name: 'bob')
+ assert bird.persisted?
+ assert_equal 'green', bird.color
+
+ assert_equal bird, Bird.create_with(color: 'blue').find_or_create_by(name: 'bob')
+ end
+
+ def test_find_or_create_by!
+ assert_raises(ActiveRecord::RecordInvalid) { Bird.find_or_create_by!(color: 'green') }
+ end
+
+ def test_find_or_initialize_by
+ assert_nil Bird.find_by(name: 'bob')
+
+ bird = Bird.find_or_initialize_by(name: 'bob')
+ assert bird.new_record?
+ bird.save!
+
+ assert_equal bird, Bird.find_or_initialize_by(name: 'bob')
+ end
+
def test_explicit_create_scope
hens = Bird.where(:name => 'hen')
assert_equal 'hen', hens.new.name
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 80f46c6b08..5f13124e5b 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -2,13 +2,9 @@ require "cases/helper"
class SchemaDumperTest < ActiveRecord::TestCase
- def initialize(*)
- super
- ActiveRecord::SchemaMigration.create_table
- end
-
def setup
super
+ ActiveRecord::SchemaMigration.create_table
@stream = StringIO.new
end
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index dc47d40f41..562ca8d9ff 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -136,7 +136,7 @@ class StoreTest < ActiveRecord::TestCase
end
test "all stored attributes are returned" do
- assert_equal [:color, :homepage, :favorite_food, :phone_number], Admin::User.stored_attributes[:settings]
+ assert_equal [:color, :homepage, :favorite_food], Admin::User.stored_attributes[:settings]
end
test "stores_attributes are class level settings" do
diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
index a8e513d81f..174d96aa4e 100644
--- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
@@ -13,7 +13,7 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
I18n.load_path.clear
I18n.backend = I18n::Backend::Simple.new
yield
- ensure
+ ensure
I18n.load_path.replace @old_load_path
I18n.backend = @old_backend
end
@@ -54,4 +54,9 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
end
end
+ test "translation for 'taken' can be overridden" do
+ I18n.backend.store_translations "en", {errors: {attributes: {title: {taken: "Custom taken message" }}}}
+ assert_equal "Custom taken message", @topic.errors.generate_message(:title, :taken, :value => 'title')
+ end
+
end
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index 15b97c02c8..efa0c9b934 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -43,7 +43,7 @@ class I18nValidationTest < ActiveRecord::TestCase
[ "given option that is not reserved", {:format => "jpg"}, {:format => "jpg" }]
# TODO Add :on case, but below doesn't work, because then the validation isn't run for some reason
# even when using .save instead .valid?
- # [ "given on condition", {:on => :save}, {}]
+ # [ "given on condition", {on: :save}, {}]
]
# validates_uniqueness_of w/ mocha
diff --git a/activerecord/test/cases/validations/presence_validation_test.rb b/activerecord/test/cases/validations/presence_validation_test.rb
index cd9175f454..1de8934406 100644
--- a/activerecord/test/cases/validations/presence_validation_test.rb
+++ b/activerecord/test/cases/validations/presence_validation_test.rb
@@ -18,6 +18,13 @@ class PresenceValidationTest < ActiveRecord::TestCase
assert b.valid?
end
+ def test_validates_presence_of_has_one
+ Boy.validates_presence_of(:face)
+ b = Boy.new
+ assert b.invalid?, "should not be valid if has_one association missing"
+ assert_equal 1, b.errors[:face].size, "validates_presence_of should only add one error"
+ end
+
def test_validates_presence_of_has_one_marked_for_destruction
Boy.validates_presence_of(:face)
b = Boy.new
diff --git a/activerecord/test/fixtures/mateys.yml b/activerecord/test/fixtures/mateys.yml
index d3690955fc..3f0405aaf8 100644
--- a/activerecord/test/fixtures/mateys.yml
+++ b/activerecord/test/fixtures/mateys.yml
@@ -1,4 +1,4 @@
blackbeard_to_redbeard:
- pirate_id: <%= ActiveRecord::Fixtures.identify(:blackbeard) %>
- target_id: <%= ActiveRecord::Fixtures.identify(:redbeard) %>
+ pirate_id: <%= ActiveRecord::FixtureSet.identify(:blackbeard) %>
+ target_id: <%= ActiveRecord::FixtureSet.identify(:redbeard) %>
weight: 10
diff --git a/activerecord/test/fixtures/parrots_pirates.yml b/activerecord/test/fixtures/parrots_pirates.yml
index 66472243c7..e1a301b91a 100644
--- a/activerecord/test/fixtures/parrots_pirates.yml
+++ b/activerecord/test/fixtures/parrots_pirates.yml
@@ -1,7 +1,7 @@
george_blackbeard:
- parrot_id: <%= ActiveRecord::Fixtures.identify(:george) %>
- pirate_id: <%= ActiveRecord::Fixtures.identify(:blackbeard) %>
+ parrot_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
+ pirate_id: <%= ActiveRecord::FixtureSet.identify(:blackbeard) %>
louis_blackbeard:
- parrot_id: <%= ActiveRecord::Fixtures.identify(:louis) %>
- pirate_id: <%= ActiveRecord::Fixtures.identify(:blackbeard) %>
+ parrot_id: <%= ActiveRecord::FixtureSet.identify(:louis) %>
+ pirate_id: <%= ActiveRecord::FixtureSet.identify(:blackbeard) %>
diff --git a/activerecord/test/fixtures/peoples_treasures.yml b/activerecord/test/fixtures/peoples_treasures.yml
index a72b190d0c..46abe50e6c 100644
--- a/activerecord/test/fixtures/peoples_treasures.yml
+++ b/activerecord/test/fixtures/peoples_treasures.yml
@@ -1,3 +1,3 @@
michael_diamond:
- rich_person_id: <%= ActiveRecord::Fixtures.identify(:michael) %>
- treasure_id: <%= ActiveRecord::Fixtures.identify(:diamond) %>
+ rich_person_id: <%= ActiveRecord::FixtureSet.identify(:michael) %>
+ treasure_id: <%= ActiveRecord::FixtureSet.identify(:diamond) %>
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index 35170faa76..467f3ccd39 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -1,7 +1,7 @@
class Admin::User < ActiveRecord::Base
belongs_to :account
store :settings, :accessors => [ :color, :homepage ]
- store_accessor :settings, :favorite_food, :phone_number
+ store_accessor :settings, :favorite_food
store :preferences, :accessors => [ :remember_login ]
store :json_data, :accessors => [ :height, :weight ], :coder => JSON
store :json_data_empty, :accessors => [ :is_a_good_guy ], :coder => JSON
diff --git a/activerecord/test/models/subscription.rb b/activerecord/test/models/subscription.rb
index 4bdb36ea46..bcac4738a3 100644
--- a/activerecord/test/models/subscription.rb
+++ b/activerecord/test/models/subscription.rb
@@ -1,4 +1,4 @@
class Subscription < ActiveRecord::Base
- belongs_to :subscriber
+ belongs_to :subscriber, :counter_cache => :books_count
belongs_to :book
end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 2cd9f30b59..d0e7338f15 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -12,6 +12,8 @@ ActiveRecord::Schema.define do
execute 'DROP FUNCTION IF EXISTS partitioned_insert_trigger()'
+ execute "DROP SCHEMA IF EXISTS schema_1 CASCADE"
+
%w(accounts_id_seq developers_id_seq projects_id_seq topics_id_seq customers_id_seq orders_id_seq).each do |seq_name|
execute "SELECT setval('#{seq_name}', 100)"
end
@@ -37,7 +39,12 @@ ActiveRecord::Schema.define do
);
_SQL
- execute <<_SQL
+ execute "CREATE SCHEMA schema_1"
+ execute "CREATE DOMAIN schema_1.text AS text"
+ execute "CREATE DOMAIN schema_1.varchar AS varchar"
+ execute "CREATE DOMAIN schema_1.bpchar AS bpchar"
+
+ execute <<_SQL
CREATE TABLE geometrics (
id serial primary key,
a_point point,
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 798ea20efc..2e4ec96933 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -621,6 +621,7 @@ ActiveRecord::Schema.define do
create_table :subscribers, :force => true, :id => false do |t|
t.string :nick, :null => false
t.string :name
+ t.column :books_count, :integer, :null => false, :default => 0
end
add_index :subscribers, :nick, :unique => true
diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb
index 92736e0ca9..bea894a583 100644
--- a/activerecord/test/support/connection.rb
+++ b/activerecord/test/support/connection.rb
@@ -13,7 +13,7 @@ module ARTest
def self.connect
puts "Using #{connection_name}"
- ActiveRecord::Model.logger = ActiveSupport::Logger.new("debug.log")
+ ActiveRecord::Model.logger = ActiveSupport::Logger.new("debug.log", 0, 100 * 1024 * 1024)
ActiveRecord::Model.configurations = connection_config
ActiveRecord::Model.establish_connection 'arunit'
ARUnit2Model.establish_connection 'arunit2'
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 2df3d1f69b..23e2ce0b03 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,18 @@
## Rails 4.0.0 (unreleased) ##
+* Hash#extract! returns only those keys that present in the receiver.
+
+ {:a => 1, :b => 2}.extract!(:a, :x) # => {:a => 1}
+
+ *Mikhail Dieterle*
+
+* Hash#extract! returns the same subclass, that the receiver is. I.e.
+ HashWithIndifferentAccess#extract! returns HashWithIndifferentAccess instance.
+
+ *Mikhail Dieterle*
+
+* Optimize ActiveSupport::Cache::Entry to reduce memory and processing overhead. *Brian Durand*
+
* Tests tag the Rails log with the current test class and test case:
[SessionsControllerTest] [test_0002_sign in] Processing by SessionsController#create as HTML
@@ -237,4 +250,6 @@
* Remove deprecated ActiveSupport::JSON::Variable. *Erich Menge*
+* Optimize log subscribers to check log level before doing any processing. *Brian Durand*
+
Please check [3-2-stable](https://github.com/rails/rails/blob/3-2-stable/activesupport/CHANGELOG.md) for previous changes.
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index fbe82c3de5..30f4ded005 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -22,5 +22,5 @@ Gem::Specification.new do |s|
s.add_dependency('i18n', '~> 0.6')
s.add_dependency('multi_json', '~> 1.3')
s.add_dependency('tzinfo', '~> 0.3.33')
- s.add_dependency('minitest', '~> 4.0')
+ s.add_dependency('minitest', '~> 4.1')
end
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 41d77ab6c1..4e397ea110 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -48,6 +48,7 @@ module ActiveSupport
autoload :Gzip
autoload :Inflector
autoload :JSON
+ autoload :KeyGenerator
autoload :MessageEncryptor
autoload :MessageVerifier
autoload :Multibyte
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index 53d05c3817..f1aff8a8e3 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -34,7 +34,7 @@ module ActiveSupport
# Returns the backtrace after all filters and silencers have been run
# against it. Filters run first, then silencers.
def clean(backtrace, kind = :silent)
- filtered = filter(backtrace)
+ filtered = filter_backtrace(backtrace)
case kind
when :silent
@@ -45,6 +45,7 @@ module ActiveSupport
filtered
end
end
+ alias :filter :clean
# Adds a filter from the block provided. Each line in the backtrace will be
# mapped against this filter.
@@ -76,7 +77,7 @@ module ActiveSupport
end
private
- def filter(backtrace)
+ def filter_backtrace(backtrace)
@filters.each do |f|
backtrace = backtrace.map { |line| f.call(line) }
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index f98ba16cdd..732ab4b7df 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -284,7 +284,9 @@ module ActiveSupport
end
if entry && entry.expired?
race_ttl = options[:race_condition_ttl].to_i
- if race_ttl and Time.now.to_f - entry.expires_at <= race_ttl
+ if race_ttl && (Time.now - entry.expires_at <= race_ttl)
+ # When an entry has :race_condition_ttl defined, put the stale entry back into the cache
+ # for a brief period while the entry is begin recalculated.
entry.expires_at = Time.now + race_ttl
write_entry(key, entry, :expires_in => race_ttl * 2)
else
@@ -532,102 +534,122 @@ module ActiveSupport
end
end
- # Entry that is put into caches. It supports expiration time on entries and
- # can compress values to save space in the cache.
- class Entry
- attr_reader :created_at, :expires_in
-
+ # This class is used to represent cache entries. Cache entries have a value and an optional
+ # expiration time. The expiration time is used to support the :race_condition_ttl option
+ # on the cache.
+ #
+ # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
+ # using short instance variable names that are lazily defined.
+ class Entry # :nodoc:
DEFAULT_COMPRESS_LIMIT = 16.kilobytes
- class << self
- # Create an entry with internal attributes set. This method is intended
- # to be used by implementations that store cache entries in a native
- # format instead of as serialized Ruby objects.
- def create(raw_value, created_at, options = {})
- entry = new(nil)
- entry.instance_variable_set(:@value, raw_value)
- entry.instance_variable_set(:@created_at, created_at.to_f)
- entry.instance_variable_set(:@compressed, options[:compressed])
- entry.instance_variable_set(:@expires_in, options[:expires_in])
- entry
- end
- end
-
# Create a new cache entry for the specified value. Options supported are
# +:compress+, +:compress_threshold+, and +:expires_in+.
def initialize(value, options = {})
- @compressed = false
- @expires_in = options[:expires_in]
- @expires_in = @expires_in.to_f if @expires_in
- @created_at = Time.now.to_f
- if value.nil?
- @value = nil
+ if should_compress?(value, options)
+ @v = compress(value)
+ @c = true
else
- @value = Marshal.dump(value)
- if should_compress?(@value, options)
- @value = Zlib::Deflate.deflate(@value)
- @compressed = true
- end
+ @v = value
end
- end
-
- # Get the raw value. This value may be serialized and compressed.
- def raw_value
- @value
- end
-
- # Get the value stored in the cache.
- def value
- # If the original value was exactly false @value is still true because
- # it is marshalled and eventually compressed. Both operations yield
- # strings.
- if @value
- Marshal.load(compressed? ? Zlib::Inflate.inflate(@value) : @value)
+ if expires_in = options[:expires_in]
+ @x = (Time.now + expires_in).to_i
end
end
- def compressed?
- @compressed
+ def value
+ convert_version_3_entry! if defined?(@value)
+ compressed? ? uncompress(@v) : @v
end
# Check if the entry is expired. The +expires_in+ parameter can override
# the value set when the entry was created.
def expired?
- @expires_in && @created_at + @expires_in <= Time.now.to_f
- end
-
- # Set a new time when the entry will expire.
- def expires_at=(time)
- if time
- @expires_in = time.to_f - @created_at
+ convert_version_3_entry! if defined?(@value)
+ if defined?(@x)
+ @x && @x < Time.now.to_i
else
- @expires_in = nil
+ false
end
end
- # Seconds since the epoch when the entry will expire.
def expires_at
- @expires_in ? @created_at + @expires_in : nil
+ Time.at(@x) if defined?(@x)
+ end
+
+ def expires_at=(value)
+ @x = value.to_i
end
# Returns the size of the cached value. This could be less than
# <tt>value.size</tt> if the data is compressed.
def size
- if @value.nil?
- 0
+ if defined?(@s)
+ @s
else
- @value.bytesize
+ case value
+ when NilClass
+ 0
+ when String
+ @v.bytesize
+ else
+ @s = Marshal.dump(@v).bytesize
+ end
+ end
+ end
+
+ # Duplicate the value in a class. This is used by cache implementations that don't natively
+ # serialize entries to protect against accidental cache modifications.
+ def dup_value!
+ convert_version_3_entry! if defined?(@value)
+ if @v && !compressed? && !(@v.is_a?(Numeric) || @v == true || @v == false)
+ if @v.is_a?(String)
+ @v = @v.dup
+ else
+ @v = Marshal.load(Marshal.dump(@v))
+ end
end
end
private
- def should_compress?(serialized_value, options)
- if options[:compress]
+ def should_compress?(value, options)
+ if value && options[:compress]
compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
- return true if serialized_value.size >= compress_threshold
+ serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
+ return true if serialized_value_size >= compress_threshold
end
false
end
+
+ def compressed?
+ defined?(@c) ? @c : false
+ end
+
+ def compress(value)
+ Zlib::Deflate.deflate(Marshal.dump(value))
+ end
+
+ def uncompress(value)
+ Marshal.load(Zlib::Inflate.inflate(value))
+ end
+
+ # The internals of this method changed between Rails 3.x and 4.0. This method provides the glue
+ # to ensure that cache entries created under the old version still work with the new class definition.
+ def convert_version_3_entry!
+ if defined?(@value)
+ @v = @value
+ remove_instance_variable(:@value)
+ end
+ if defined?(@compressed)
+ @c = @compressed
+ remove_instance_variable(:@compressed)
+ end
+ if defined?(@expires_in) && defined?(@created_at) && @expires_in && @created_at
+ @x = (@created_at + @expires_in).to_i
+ remove_instance_variable(:@created_at)
+ remove_instance_variable(:@expires_in)
+ end
+ end
end
end
end
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 5aa78cc9f3..17450fe4d0 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -132,6 +132,10 @@ module ActiveSupport
method = options && options[:unless_exist] ? :add : :set
value = options[:raw] ? entry.value.to_s : entry
expires_in = options[:expires_in].to_i
+ if expires_in > 0 && !options[:raw]
+ # Set the memcache expire a few minutes in the future to support race condition ttls on read
+ expires_in += 5.minutes
+ end
@data.send(method, escape_key(key), value, expires_in, options)
rescue Dalli::DalliError => e
logger.error("DalliError (#{e}): #{e.message}") if logger
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index 7fd5e3b53d..4d26fb7e42 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -135,6 +135,7 @@ module ActiveSupport
end
def write_entry(key, entry, options) # :nodoc:
+ entry.dup_value!
synchronize do
old_entry = @data[key]
return false if @data.key?(key) && options[:unless_exist]
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 7f37c459c1..6a0c4a015a 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -1,6 +1,8 @@
require 'active_support/xml_mini'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/string/inflections'
+require 'active_support/core_ext/object/to_param'
+require 'active_support/core_ext/object/to_query'
class Array
# Converts the array to a comma-separated sentence where the last element is
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index a7551d9c64..02ae57b4a6 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -28,7 +28,7 @@ class Date
# Returns week start day symbol (e.g. :monday), or raises an ArgumentError for invalid day symbol.
def find_beginning_of_week!(week_start)
- raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.keys.include?(week_start)
+ raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start)
week_start
end
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index 5ba8197006..828f03ad62 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -1,8 +1,10 @@
require 'active_support/xml_mini'
require 'active_support/time'
+require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/object/to_param'
+require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/reverse_merge'
-require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/inflections'
class Hash
diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb
index 45fec57009..de09f26f3d 100644
--- a/activesupport/lib/active_support/core_ext/hash/slice.rb
+++ b/activesupport/lib/active_support/core_ext/hash/slice.rb
@@ -32,9 +32,9 @@ class Hash
# Removes and returns the key/value pairs matching the given keys.
#
- # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b)
- # # => {:a => 1, :b => 2}
+ # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => { a: 1, b: 2 }
+ # { a: 1, b: 2 }.extract!(:a, :x) # => { a: 1 }
def extract!(*keys)
- keys.each_with_object({}) { |key, result| result[key] = delete(key) }
+ keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
end
end
diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb
index b3f5fde335..6c15fffc0f 100644
--- a/activesupport/lib/active_support/deprecation.rb
+++ b/activesupport/lib/active_support/deprecation.rb
@@ -1,15 +1,24 @@
-require 'active_support/core_ext/module/deprecation'
-require 'active_support/deprecation/instance_delegator'
-require 'active_support/deprecation/behaviors'
-require 'active_support/deprecation/reporting'
-require 'active_support/deprecation/method_wrappers'
-require 'active_support/deprecation/proxy_wrappers'
require 'singleton'
module ActiveSupport
# \Deprecation specifies the API used by Rails to deprecate methods, instance
# variables, objects and constants.
class Deprecation
+ # active_support.rb sets an autoload for ActiveSupport::Deprecation.
+ #
+ # If these requires were at the top of the file the constant would not be
+ # defined by the time their files were loaded. Since some of them reopen
+ # ActiveSupport::Deprecation its autoload would be triggered, resulting in
+ # a circular require warning for active_support/deprecation.rb.
+ #
+ # So, we define the constant first, and load dependencies later.
+ require 'active_support/deprecation/instance_delegator'
+ require 'active_support/deprecation/behaviors'
+ require 'active_support/deprecation/reporting'
+ require 'active_support/deprecation/method_wrappers'
+ require 'active_support/deprecation/proxy_wrappers'
+ require 'active_support/core_ext/module/deprecation'
+
include Singleton
include InstanceDelegator
include Behavior
diff --git a/activesupport/lib/active_support/descendants_tracker.rb b/activesupport/lib/active_support/descendants_tracker.rb
index e2a8b4d4e3..27861e01d0 100644
--- a/activesupport/lib/active_support/descendants_tracker.rb
+++ b/activesupport/lib/active_support/descendants_tracker.rb
@@ -2,35 +2,50 @@ module ActiveSupport
# This module provides an internal implementation to track descendants
# which is faster than iterating through ObjectSpace.
module DescendantsTracker
- @@direct_descendants = Hash.new { |h, k| h[k] = [] }
+ @@direct_descendants = {}
- def self.direct_descendants(klass)
- @@direct_descendants[klass]
- end
+ class << self
+ def direct_descendants(klass)
+ @@direct_descendants[klass] || []
+ end
- def self.descendants(klass)
- @@direct_descendants[klass].inject([]) do |descendants, _klass|
- descendants << _klass
- descendants.concat _klass.descendants
+ def descendants(klass)
+ arr = []
+ accumulate_descendants(klass, arr)
+ arr
end
- end
- def self.clear
- if defined? ActiveSupport::Dependencies
- @@direct_descendants.each do |klass, descendants|
- if ActiveSupport::Dependencies.autoloaded?(klass)
- @@direct_descendants.delete(klass)
- else
- descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
+ def clear
+ if defined? ActiveSupport::Dependencies
+ @@direct_descendants.each do |klass, descendants|
+ if ActiveSupport::Dependencies.autoloaded?(klass)
+ @@direct_descendants.delete(klass)
+ else
+ descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
+ end
end
+ else
+ @@direct_descendants.clear
+ end
+ end
+
+ # This is the only method that is not thread safe, but is only ever called
+ # during the eager loading phase.
+ def store_inherited(klass, descendant)
+ (@@direct_descendants[klass] ||= []) << descendant
+ end
+
+ private
+ def accumulate_descendants(klass, acc)
+ if direct_descendants = @@direct_descendants[klass]
+ acc.concat(direct_descendants)
+ direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
end
- else
- @@direct_descendants.clear
end
end
def inherited(base)
- self.direct_descendants << base
+ DescendantsTracker.store_inherited(self, base)
super
end
diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb
new file mode 100644
index 0000000000..04d170f801
--- /dev/null
+++ b/activesupport/lib/active_support/key_generator.rb
@@ -0,0 +1,23 @@
+require 'openssl'
+
+module ActiveSupport
+ # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2
+ # It can be used to derive a number of keys for various purposes from a given secret.
+ # This lets rails applications have a single secure secret, but avoid reusing that
+ # key in multiple incompatible contexts.
+ class KeyGenerator
+ def initialize(secret, options = {})
+ @secret = secret
+ # The default iterations are higher than required for our key derivation uses
+ # on the off chance someone uses this for password storage
+ @iterations = options[:iterations] || 2**16
+ end
+
+ # Returns a derived key suitable for use. The default key_size is chosen
+ # to be compatible with the default settings of ActiveSupport::MessageVerifier.
+ # i.e. OpenSSL::Digest::SHA1#block_length
+ def generate_key(salt, key_size=64)
+ OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/queueing.rb b/activesupport/lib/active_support/queueing.rb
index 7c352886f3..c8ba28021d 100644
--- a/activesupport/lib/active_support/queueing.rb
+++ b/activesupport/lib/active_support/queueing.rb
@@ -56,32 +56,6 @@ module ActiveSupport
end
end
- # A container for multiple queues. This class delegates to a default Queue
- # so that <tt>Rails.queue.push</tt> and friends will Just Work. To use this class
- # with multiple queues:
- #
- # # In your configuration:
- # Rails.queue[:image_queue] = SomeQueue.new
- # Rails.queue[:mail_queue] = SomeQueue.new
- #
- # # In your app code:
- # Rails.queue[:mail_queue].push SomeJob.new
- #
- class QueueContainer < DelegateClass(::Queue)
- def initialize(default_queue)
- @queues = { :default => default_queue }
- super(default_queue)
- end
-
- def [](queue_name)
- @queues[queue_name]
- end
-
- def []=(queue_name, queue)
- @queues[queue_name] = queue
- end
- end
-
# The threaded consumer will run jobs in a background thread in
# development mode or in a VM where running jobs on a thread in
# production mode makes sense.
@@ -90,10 +64,6 @@ module ActiveSupport
# queue and joins the thread, which will ensure that all jobs
# are executed before the process finally dies.
class ThreadedQueueConsumer
- def self.start(*args)
- new(*args).start
- end
-
def initialize(queue, options = {})
@queue = queue
@logger = options[:logger]
diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb
index 12aef0d7fe..7c149df1e4 100644
--- a/activesupport/lib/active_support/testing/performance/ruby.rb
+++ b/activesupport/lib/active_support/testing/performance/ruby.rb
@@ -2,7 +2,7 @@ begin
require 'ruby-prof'
rescue LoadError
$stderr.puts 'Specify ruby-prof as application\'s dependency in Gemfile to run benchmarks.'
- exit
+ raise
end
module ActiveSupport
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 71cd9d81b3..9f76f4c90b 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -3,6 +3,20 @@ require 'abstract_unit'
require 'active_support/cache'
class CacheKeyTest < ActiveSupport::TestCase
+ def test_entry_legacy_optional_ivars
+ legacy = Class.new(ActiveSupport::Cache::Entry) do
+ def initialize(value, options = {})
+ @value = value
+ @expires_in = nil
+ @created_at = nil
+ super
+ end
+ end
+
+ entry = legacy.new 'foo'
+ assert_equal 'foo', entry.value
+ end
+
def test_expand_cache_key
assert_equal '1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true])
assert_equal 'name/1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true], :name)
@@ -224,25 +238,22 @@ module CacheStoreBehavior
end
def test_read_multi_with_expires
- @cache.write('foo', 'bar', :expires_in => 0.001)
+ time = Time.now
+ @cache.write('foo', 'bar', :expires_in => 10)
@cache.write('fu', 'baz')
@cache.write('fud', 'biz')
- sleep(0.002)
+ Time.stubs(:now).returns(time + 11)
assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
end
def test_read_and_write_compressed_small_data
@cache.write('foo', 'bar', :compress => true)
- raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
assert_equal 'bar', @cache.read('foo')
- assert_equal 'bar', Marshal.load(raw_value)
end
def test_read_and_write_compressed_large_data
@cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
- raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
assert_equal 'bar', @cache.read('foo')
- assert_equal 'bar', Marshal.load(Zlib::Inflate.inflate(raw_value))
end
def test_read_and_write_compressed_nil
@@ -301,14 +312,6 @@ module CacheStoreBehavior
assert !@cache.exist?('foo')
end
- def test_read_should_return_a_different_object_id_each_time_it_is_called
- @cache.write('foo', 'bar')
- assert_not_equal @cache.read('foo').object_id, @cache.read('foo').object_id
- value = @cache.read('foo')
- value << 'bingo'
- assert_not_equal value, @cache.read('foo')
- end
-
def test_original_store_objects_should_not_be_immutable
bar = 'bar'
@cache.write('foo', bar)
@@ -363,7 +366,7 @@ module CacheStoreBehavior
rescue ArgumentError
end
assert_equal "bar", @cache.read('foo')
- Time.stubs(:now).returns(time + 71)
+ Time.stubs(:now).returns(time + 91)
assert_nil @cache.read('foo')
end
@@ -627,7 +630,7 @@ end
class MemoryStoreTest < ActiveSupport::TestCase
def setup
- @record_size = Marshal.dump("aaaaaaaaaa").bytesize
+ @record_size = ActiveSupport::Cache::Entry.new("aaaaaaaaaa").size
@cache = ActiveSupport::Cache.lookup_store(:memory_store, :expires_in => 60, :size => @record_size * 10)
end
@@ -646,9 +649,9 @@ class MemoryStoreTest < ActiveSupport::TestCase
@cache.prune(@record_size * 3)
assert @cache.exist?(5)
assert @cache.exist?(4)
- assert !@cache.exist?(3)
+ assert !@cache.exist?(3), "no entry"
assert @cache.exist?(2)
- assert !@cache.exist?(1)
+ assert !@cache.exist?(1), "no entry"
end
def test_prune_size_on_write
@@ -670,12 +673,12 @@ class MemoryStoreTest < ActiveSupport::TestCase
assert @cache.exist?(9)
assert @cache.exist?(8)
assert @cache.exist?(7)
- assert !@cache.exist?(6)
- assert !@cache.exist?(5)
+ assert !@cache.exist?(6), "no entry"
+ assert !@cache.exist?(5), "no entry"
assert @cache.exist?(4)
- assert !@cache.exist?(3)
+ assert !@cache.exist?(3), "no entry"
assert @cache.exist?(2)
- assert !@cache.exist?(1)
+ assert !@cache.exist?(1), "no entry"
end
def test_pruning_is_capped_at_a_max_time
@@ -764,6 +767,14 @@ class MemCacheStoreTest < ActiveSupport::TestCase
assert_equal [], cache.read("foo")
end
end
+
+ def test_read_should_return_a_different_object_id_each_time_it_is_called
+ @cache.write('foo', 'bar')
+ assert_not_equal @cache.read('foo').object_id, @cache.read('foo').object_id
+ value = @cache.read('foo')
+ value << 'bingo'
+ assert_not_equal value, @cache.read('foo')
+ end
end
class NullStoreTest < ActiveSupport::TestCase
@@ -844,15 +855,6 @@ class CacheStoreLoggerTest < ActiveSupport::TestCase
end
class CacheEntryTest < ActiveSupport::TestCase
- def test_create_raw_entry
- time = Time.now
- entry = ActiveSupport::Cache::Entry.create("raw", time, :compress => false, :expires_in => 300)
- assert_equal "raw", entry.raw_value
- assert_equal time.to_f, entry.created_at
- assert !entry.compressed?
- assert_equal 300, entry.expires_in
- end
-
def test_expired
entry = ActiveSupport::Cache::Entry.new("value")
assert !entry.expired?, 'entry not expired'
@@ -864,16 +866,43 @@ class CacheEntryTest < ActiveSupport::TestCase
end
def test_compress_values
- entry = ActiveSupport::Cache::Entry.new("value", :compress => true, :compress_threshold => 1)
- assert_equal "value", entry.value
- assert entry.compressed?
- assert_equal "value", Marshal.load(Zlib::Inflate.inflate(entry.raw_value))
+ value = "value" * 100
+ entry = ActiveSupport::Cache::Entry.new(value, :compress => true, :compress_threshold => 1)
+ assert_equal value, entry.value
+ assert(value.bytesize > entry.size, "value is compressed")
end
def test_non_compress_values
- entry = ActiveSupport::Cache::Entry.new("value")
- assert_equal "value", entry.value
- assert_equal "value", Marshal.load(entry.raw_value)
- assert !entry.compressed?
+ value = "value" * 100
+ entry = ActiveSupport::Cache::Entry.new(value)
+ assert_equal value, entry.value
+ assert_equal value.bytesize, entry.size
+ end
+
+ def test_restoring_version_3_entries
+ version_3_entry = ActiveSupport::Cache::Entry.allocate
+ version_3_entry.instance_variable_set(:@value, "hello")
+ version_3_entry.instance_variable_set(:@created_at, Time.now - 60)
+ entry = Marshal.load(Marshal.dump(version_3_entry))
+ assert_equal "hello", entry.value
+ assert_equal false, entry.expired?
+ end
+
+ def test_restoring_compressed_version_3_entries
+ version_3_entry = ActiveSupport::Cache::Entry.allocate
+ version_3_entry.instance_variable_set(:@value, Zlib::Deflate.deflate(Marshal.dump("hello")))
+ version_3_entry.instance_variable_set(:@compressed, true)
+ entry = Marshal.load(Marshal.dump(version_3_entry))
+ assert_equal "hello", entry.value
+ end
+
+ def test_restoring_expired_version_3_entries
+ version_3_entry = ActiveSupport::Cache::Entry.allocate
+ version_3_entry.instance_variable_set(:@value, "hello")
+ version_3_entry.instance_variable_set(:@created_at, Time.now - 60)
+ version_3_entry.instance_variable_set(:@expires_in, 58.9)
+ entry = Marshal.load(Marshal.dump(version_3_entry))
+ assert_equal "hello", entry.value
+ assert_equal true, entry.expired?
end
end
diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb
index b7c3b130c3..8810302f40 100644
--- a/activesupport/test/callbacks_test.rb
+++ b/activesupport/test/callbacks_test.rb
@@ -120,7 +120,7 @@ module CallbacksTest
end
class Child < ParentController
- skip_callback :dispatch, :before, :log, :if => proc {|c| c.action_name == :update}
+ skip_callback :dispatch, :before, :log, :if => proc {|c| c.action_name == :update}
skip_callback :dispatch, :after, :log2
end
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 01934dd2c3..7cfe7b0ea7 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -724,8 +724,32 @@ class HashExtTest < ActiveSupport::TestCase
def test_extract
original = {:a => 1, :b => 2, :c => 3, :d => 4}
expected = {:a => 1, :b => 2}
+ remaining = {:c => 3, :d => 4}
- assert_equal expected, original.extract!(:a, :b)
+ assert_equal expected, original.extract!(:a, :b, :x)
+ assert_equal remaining, original
+ end
+
+ def test_extract_nils
+ original = {:a => nil, :b => nil}
+ expected = {:a => nil}
+ extracted = original.extract!(:a, :x)
+
+ assert_equal expected, extracted
+ assert_equal nil, extracted[:a]
+ assert_equal nil, extracted[:x]
+ end
+
+ def test_indifferent_extract
+ original = {:a => 1, 'b' => 2, :c => 3, 'd' => 4}.with_indifferent_access
+ expected = {:a => 1, :b => 2}.with_indifferent_access
+ remaining = {:c => 3, :d => 4}.with_indifferent_access
+
+ [['a', 'b'], [:a, :b]].each do |keys|
+ copy = original.dup
+ assert_equal expected, copy.extract!(*keys)
+ assert_equal remaining, copy
+ end
end
def test_except
diff --git a/activesupport/test/descendants_tracker_test_cases.rb b/activesupport/test/descendants_tracker_test_cases.rb
index 066ec8549b..69e046998e 100644
--- a/activesupport/test/descendants_tracker_test_cases.rb
+++ b/activesupport/test/descendants_tracker_test_cases.rb
@@ -1,3 +1,5 @@
+require 'set'
+
module DescendantsTrackerTestCases
class Parent
extend ActiveSupport::DescendantsTracker
@@ -18,15 +20,15 @@ module DescendantsTrackerTestCases
ALL = [Parent, Child1, Child2, Grandchild1, Grandchild2]
def test_descendants
- assert_equal [Child1, Grandchild1, Grandchild2, Child2], Parent.descendants
- assert_equal [Grandchild1, Grandchild2], Child1.descendants
- assert_equal [], Child2.descendants
+ assert_equal_sets [Child1, Grandchild1, Grandchild2, Child2], Parent.descendants
+ assert_equal_sets [Grandchild1, Grandchild2], Child1.descendants
+ assert_equal_sets [], Child2.descendants
end
def test_direct_descendants
- assert_equal [Child1, Child2], Parent.direct_descendants
- assert_equal [Grandchild1, Grandchild2], Child1.direct_descendants
- assert_equal [], Child2.direct_descendants
+ assert_equal_sets [Child1, Child2], Parent.direct_descendants
+ assert_equal_sets [Grandchild1, Grandchild2], Child1.direct_descendants
+ assert_equal_sets [], Child2.direct_descendants
end
def test_clear
@@ -40,6 +42,10 @@ module DescendantsTrackerTestCases
protected
+ def assert_equal_sets(expected, actual)
+ assert_equal Set.new(expected), Set.new(actual)
+ end
+
def mark_as_autoloaded(*klasses)
# If ActiveSupport::Dependencies is not loaded, forget about autoloading.
# This allows using AS::DescendantsTracker without AS::Dependencies.
@@ -56,4 +62,4 @@ module DescendantsTrackerTestCases
ActiveSupport::Dependencies.autoloaded_constants = old_autoloaded if defined? ActiveSupport::Dependencies
ActiveSupport::DescendantsTracker.class_eval("@@direct_descendants").replace(old_descendants)
end
-end \ No newline at end of file
+end
diff --git a/activesupport/test/descendants_tracker_with_autoloading_test.rb b/activesupport/test/descendants_tracker_with_autoloading_test.rb
index 9180f1f977..ab1be296f8 100644
--- a/activesupport/test/descendants_tracker_with_autoloading_test.rb
+++ b/activesupport/test/descendants_tracker_with_autoloading_test.rb
@@ -18,17 +18,17 @@ class DescendantsTrackerWithAutoloadingTest < ActiveSupport::TestCase
def test_clear_with_autoloaded_children_and_granchildren
mark_as_autoloaded Child1, Grandchild1, Grandchild2 do
ActiveSupport::DescendantsTracker.clear
- assert_equal [Child2], Parent.descendants
- assert_equal [], Child2.descendants
+ assert_equal_sets [Child2], Parent.descendants
+ assert_equal_sets [], Child2.descendants
end
end
def test_clear_with_autoloaded_granchildren
mark_as_autoloaded Grandchild1, Grandchild2 do
ActiveSupport::DescendantsTracker.clear
- assert_equal [Child1, Child2], Parent.descendants
- assert_equal [], Child1.descendants
- assert_equal [], Child2.descendants
+ assert_equal_sets [Child1, Child2], Parent.descendants
+ assert_equal_sets [], Child1.descendants
+ assert_equal_sets [], Child2.descendants
end
end
end
diff --git a/activesupport/test/key_generator_test.rb b/activesupport/test/key_generator_test.rb
new file mode 100644
index 0000000000..525082d313
--- /dev/null
+++ b/activesupport/test/key_generator_test.rb
@@ -0,0 +1,32 @@
+require 'abstract_unit'
+
+begin
+ require 'openssl'
+ OpenSSL::PKCS5
+rescue LoadError, NameError
+ $stderr.puts "Skipping KeyGenerator test: broken OpenSSL install"
+else
+
+require 'active_support/time'
+require 'active_support/json'
+
+class KeyGeneratorTest < ActiveSupport::TestCase
+ def setup
+ @secret = SecureRandom.hex(64)
+ @generator = ActiveSupport::KeyGenerator.new(@secret, :iterations=>2)
+ end
+
+ test "Generating a key of the default length" do
+ derived_key = @generator.generate_key("some_salt")
+ assert_kind_of String, derived_key
+ assert_equal OpenSSL::Digest::SHA1.new.block_length, derived_key.length, "Should have generated a key of the default size"
+ end
+
+ test "Generating a key of an alternative length" do
+ derived_key = @generator.generate_key("some_salt", 32)
+ assert_kind_of String, derived_key
+ assert_equal 32, derived_key.length, "Should have generated a key of the right size"
+ end
+end
+
+end
diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb
index ac85ba1f21..14ba4e0076 100644
--- a/activesupport/test/ordered_hash_test.rb
+++ b/activesupport/test/ordered_hash_test.rb
@@ -216,7 +216,6 @@ class OrderedHashTest < ActiveSupport::TestCase
alternate = ActiveSupport::OrderedHash[ [
[1, 2],
[3, 4],
- "bad key value pair",
[ 'missing value' ]
]]
diff --git a/activesupport/test/queueing/container_test.rb b/activesupport/test/queueing/container_test.rb
deleted file mode 100644
index 7afc11e7a9..0000000000
--- a/activesupport/test/queueing/container_test.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require 'abstract_unit'
-require 'active_support/queueing'
-
-module ActiveSupport
- class ContainerTest < ActiveSupport::TestCase
- def test_delegates_to_default
- q = Queue.new
- container = QueueContainer.new q
- job = Object.new
-
- container.push job
- assert_equal job, q.pop
- end
-
- def test_access_default
- q = Queue.new
- container = QueueContainer.new q
- assert_equal q, container[:default]
- end
-
- def test_assign_queue
- container = QueueContainer.new Object.new
- q = Object.new
- container[:foo] = q
- assert_equal q, container[:foo]
- end
- end
-end
diff --git a/activesupport/test/queueing/test_queue_test.rb b/activesupport/test/queueing/test_queue_test.rb
index e398a48bea..451fb68d3e 100644
--- a/activesupport/test/queueing/test_queue_test.rb
+++ b/activesupport/test/queueing/test_queue_test.rb
@@ -99,4 +99,48 @@ class TestQueueTest < ActiveSupport::TestCase
assert job.ran?, "The job runs synchronously when the queue is drained"
assert_equal job.thread_id, Thread.current.object_id
end
+
+ class IdentifiableJob
+ def initialize(id)
+ @id = id
+ end
+
+ def ==(other)
+ other.same_id?(@id)
+ end
+
+ def same_id?(other_id)
+ other_id == @id
+ end
+
+ def run
+ end
+ end
+
+ def test_queue_can_be_observed
+ jobs = (1..10).map do |id|
+ IdentifiableJob.new(id)
+ end
+
+ jobs.each do |job|
+ @queue.push job
+ end
+
+ assert_equal jobs, @queue.jobs
+ end
+
+ def test_adding_an_unmarshallable_job
+ anonymous_class_instance = Struct.new(:run).new
+
+ assert_raises TypeError do
+ @queue.push anonymous_class_instance
+ end
+ end
+
+ def test_attempting_to_add_a_reference_to_itself
+ job = {reference: @queue}
+ assert_raises TypeError do
+ @queue.push job
+ end
+ end
end
diff --git a/activesupport/test/testing/performance_test.rb b/activesupport/test/testing/performance_test.rb
index 53073cb8db..6918110cce 100644
--- a/activesupport/test/testing/performance_test.rb
+++ b/activesupport/test/testing/performance_test.rb
@@ -1,10 +1,19 @@
require 'abstract_unit'
-require 'active_support/testing/performance'
-
module ActiveSupport
module Testing
class PerformanceTest < ActiveSupport::TestCase
+ begin
+ require 'active_support/testing/performance'
+ HAVE_RUBYPROF = true
+ rescue LoadError
+ HAVE_RUBYPROF = false
+ end
+
+ def setup
+ skip "no rubyprof" unless HAVE_RUBYPROF
+ end
+
def test_amount_format
amount_metric = ActiveSupport::Testing::Performance::Metrics[:amount].new
assert_equal "0", amount_metric.format(0)
@@ -56,4 +65,4 @@ module ActiveSupport
end
end
end
-end \ No newline at end of file
+end
diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md
new file mode 100644
index 0000000000..a1a480e911
--- /dev/null
+++ b/guides/CHANGELOG.md
@@ -0,0 +1,5 @@
+## Rails 4.0.0 (unreleased) ##
+
+* Guides updated to reflect new test locations. *Mike Moore*
+
+* Guides have a responsive design. *Joe Fiorini*
diff --git a/guides/assets/javascripts/guides.js b/guides/assets/javascripts/guides.js
index c4e4d459ea..7e494fb6d8 100644
--- a/guides/assets/javascripts/guides.js
+++ b/guides/assets/javascripts/guides.js
@@ -5,3 +5,53 @@ function guideMenu(){
document.getElementById('guides').style.display = "none";
}
}
+
+$.fn.selectGuide = function(guide){
+ $("select", this).val(guide);
+}
+
+guidesIndex = {
+ bind: function(){
+ var currentGuidePath = window.location.pathname;
+ var currentGuide = currentGuidePath.substring(currentGuidePath.lastIndexOf("/")+1);
+ $(".guides-index-small").
+ on("change", "select", guidesIndex.navigate).
+ selectGuide(currentGuide);
+ $(".more-info-button:visible").click(function(e){
+ e.stopPropagation();
+ if($(".more-info-links").is(":visible")){
+ $(".more-info-links").addClass("s-hidden").unwrap();
+ } else {
+ $(".more-info-links").wrap("<div class='more-info-container'></div>").removeClass("s-hidden");
+ }
+ $(document).on("click", function(e){
+ var $button = $(".more-info-button");
+ var element;
+
+ // Cross browser find the element that had the event
+ if (e.target) element = e.target;
+ else if (e.srcElement) element = e.srcElement;
+
+ // Defeat the older Safari bug:
+ // http://www.quirksmode.org/js/events_properties.html
+ if (element.nodeType == 3) element = element.parentNode;
+
+ var $element = $(element);
+
+ var $container = $element.parents(".more-info-container");
+
+ // We've captured a click outside the popup
+ if($container.length == 0){
+ $container = $button.next(".more-info-container");
+ $container.find(".more-info-links").addClass("s-hidden").unwrap();
+ $(document).off("click");
+ }
+ });
+ });
+ },
+ navigate: function(e){
+ var $list = $(e.target);
+ url = $list.val();
+ window.location = url;
+ }
+}
diff --git a/guides/assets/javascripts/jquery.min.js b/guides/assets/javascripts/jquery.min.js
new file mode 100644
index 0000000000..93adea19fd
--- /dev/null
+++ b/guides/assets/javascripts/jquery.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.7.2 jquery.com | jquery.org/license */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
+a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
+.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file
diff --git a/guides/assets/javascripts/responsive-tables.js b/guides/assets/javascripts/responsive-tables.js
new file mode 100755
index 0000000000..8554a1343b
--- /dev/null
+++ b/guides/assets/javascripts/responsive-tables.js
@@ -0,0 +1,43 @@
+$(document).ready(function() {
+ var switched = false;
+ $("table").not(".syntaxhighlighter").addClass("responsive");
+ var updateTables = function() {
+ if (($(window).width() < 767) && !switched ){
+ switched = true;
+ $("table.responsive").each(function(i, element) {
+ splitTable($(element));
+ });
+ return true;
+ }
+ else if (switched && ($(window).width() > 767)) {
+ switched = false;
+ $("table.responsive").each(function(i, element) {
+ unsplitTable($(element));
+ });
+ }
+ };
+
+ $(window).load(updateTables);
+ $(window).bind("resize", updateTables);
+
+
+ function splitTable(original)
+ {
+ original.wrap("<div class='table-wrapper' />");
+
+ var copy = original.clone();
+ copy.find("td:not(:first-child), th:not(:first-child)").css("display", "none");
+ copy.removeClass("responsive");
+
+ original.closest(".table-wrapper").append(copy);
+ copy.wrap("<div class='pinned' />");
+ original.wrap("<div class='scrollable' />");
+ }
+
+ function unsplitTable(original) {
+ original.closest(".table-wrapper").find(".pinned").remove();
+ original.unwrap();
+ original.unwrap();
+ }
+
+});
diff --git a/guides/assets/stylesheets/main.css b/guides/assets/stylesheets/main.css
index a30e12fe7e..9f5e101d1c 100644
--- a/guides/assets/stylesheets/main.css
+++ b/guides/assets/stylesheets/main.css
@@ -9,6 +9,9 @@
.left {float: left; margin-right: 1em;}
.right {float: right; margin-left: 1em;}
+@media screen and (max-width: 480px) {
+ .left, .right { float: none; }
+}
.small {font-size: smaller;}
.large {font-size: larger;}
.hide {display: none;}
@@ -29,6 +32,13 @@ pre,code {
font-family: "Anonymous Pro", "Inconsolata", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
line-height: 1.5;
}
+pre,tt,code,.note>p {
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+}
abbr, acronym { border-bottom: 1px dotted #666; }
address { margin: 0 0 1.5em; font-style: italic; }
@@ -52,23 +62,23 @@ textarea, select {
}
table {
- margin: 0 0 1.5em;
- border: 2px solid #CCC;
- background: #FFF;
- border-collapse: collapse;
+ margin: 0 0 1.5em;
+ border: 2px solid #CCC;
+ background: #FFF;
+ border-collapse: collapse;
}
table th, table td {
- padding: 0.25em 1em;
- border: 1px solid #CCC;
- border-collapse: collapse;
+ padding: 0.25em 1em;
+ border: 1px solid #CCC;
+ border-collapse: collapse;
}
table th {
- border-bottom: 2px solid #CCC;
- background: #EEE;
- font-weight: bold;
- padding: 0.5em 1em;
+ border-bottom: 2px solid #CCC;
+ background: #EEE;
+ font-weight: bold;
+ padding: 0.5em 1em;
}
@@ -76,84 +86,205 @@ table th {
--------------------------------------- */
body {
- text-align: center;
- font-family: Helvetica, Arial, sans-serif;
- font-size: 87.5%;
- line-height: 1.5em;
- background: #fff;
- min-width: 69em;
- color: #999;
- }
+ text-align: center;
+ font-family: Helvetica, Arial, sans-serif;
+ font-size: 87.5%;
+ line-height: 1.5em;
+ background: #fff;
+ color: #999;
+ }
.wrapper {
- text-align: left;
- margin: 0 auto;
- width: 69em;
- }
+ text-align: left;
+ margin: 0 auto;
+ max-width: 960px;
+ padding: 0 1em;
+ }
+
+.red-button {
+ display: inline-block;
+ border-top: 1px solid rgba(255,255,255,.5);
+ background: #751913;
+ background: -webkit-gradient(linear, left top, left bottom, from(#c52f24), to(#751913));
+ background: -webkit-linear-gradient(top, #c52f24, #751913);
+ background: -moz-linear-gradient(top, #c52f24, #751913);
+ background: -ms-linear-gradient(top, #c52f24, #751913);
+ background: -o-linear-gradient(top, #c52f24, #751913);
+ padding: 9px 18px;
+ -webkit-border-radius: 11px;
+ -moz-border-radius: 11px;
+ border-radius: 11px;
+ -webkit-box-shadow: rgba(0,0,0,1) 0 1px 0;
+ -moz-box-shadow: rgba(0,0,0,1) 0 1px 0;
+ box-shadow: rgba(0,0,0,1) 0 1px 0;
+ text-shadow: rgba(0,0,0,.4) 0 1px 0;
+ color: white;
+ font-size: 15px;
+ font-family: Helvetica, Arial, Sans-Serif;
+ text-decoration: none;
+ vertical-align: middle;
+}
+.red-button:active {
+ border-top: none;
+ padding-top: 10px;
+ background: -webkit-gradient(linear, left top, left bottom, from(#751913), to(#c52f24));
+ background: -webkit-linear-gradient(top, #751913, #c52f24);
+ background: -moz-linear-gradient(top, #751913, #c52f24);
+ background: -ms-linear-gradient(top, #751913, #c52f24);
+ background: -o-linear-gradient(top, #751913, #c52f24);
+}
#topNav {
- padding: 1em 0;
- color: #565656;
- background: #222;
+ padding: 1em 0;
+ color: #565656;
+ background: #222;
+}
+
+.s-hidden {
+ display: none;
+}
+
+@media screen and (min-width: 1025px) {
+ .more-info-button {
+ display: none;
+ }
+ .more-info-links {
+ list-style: none;
+ display: inline;
+ margin: 0;
+ }
+
+ .more-info {
+ display: inline-block;
+ }
+ .more-info:after {
+ content: " |";
+ }
+
+ .more-info:last-child:after {
+ content: "";
+ }
+
+}
+
+@media screen and (max-width: 1024px) {
+ #topNav .wrapper { text-align: center; }
+ .more-info-button {
+ position: relative;
+ z-index: 25;
+ }
+
+ .more-info-label {
+ display: none;
+ }
+
+ .more-info-container {
+ position: absolute;
+ top: .5em;
+ z-index: 20;
+ margin: 0 auto;
+ left: 0;
+ right: 0;
+ width: 20em;
+ }
+
+ .more-info-links {
+ display: block;
+ list-style: none;
+ background-color: #c52f24;
+ border-radius: 5px;
+ padding-top: 5.25em;
+ border: 1px #980905 solid;
+ }
+ .more-info-links.s-hidden {
+ display: none;
+ }
+ .more-info {
+ padding: .75em;
+ border-top: 1px #980905 solid;
+ }
+ .more-info a, .more-info a:link, .more-info a:visited {
+ display: block;
+ color: white;
+ width: 100%;
+ height: 100%;
+ text-decoration: none;
+ text-transform: uppercase;
+ }
}
#header {
- background: #c52f24 url(../images/header_tile.gif) repeat-x;
- color: #FFF;
- padding: 1.5em 0;
- position: relative;
- z-index: 99;
- }
+ background: #c52f24 url(../images/header_tile.gif) repeat-x;
+ color: #FFF;
+ padding: 1.5em 0;
+ z-index: 99;
+ }
#feature {
- background: #d5e9f6 url(../images/feature_tile.gif) repeat-x;
- color: #333;
- padding: 0.5em 0 1.5em;
+ background: #d5e9f6 url(../images/feature_tile.gif) repeat-x;
+ color: #333;
+ padding: 0.5em 0 1.5em;
}
#container {
- color: #333;
- padding: 0.5em 0 1.5em 0;
- }
+ color: #333;
+ padding: 0.5em 0 1.5em 0;
+ }
#mainCol {
- width: 45em;
- margin-left: 2em;
- }
+ max-width: 630px;
+ margin-left: 2em;
+ }
#subCol {
- position: absolute;
- z-index: 0;
- top: 0;
- right: 0;
- background: #FFF;
- padding: 1em 1.5em 1em 1.25em;
- width: 17em;
- font-size: 0.9285em;
- line-height: 1.3846em;
- }
+ position: absolute;
+ z-index: 0;
+ top: 0;
+ right: 0;
+ background: #FFF;
+ padding: 1em 1.5em 1em 1.25em;
+ width: 17em;
+ font-size: 0.9285em;
+ line-height: 1.3846em;
+ margin-right: 1em;
+ }
+
+
+@media screen and (max-width: 800px) {
+ #subCol {
+ position: static;
+ width: inherit;
+ margin-left: -1em;
+ margin-right: 0;
+ padding-right: 1.25em;
+ }
+}
#extraCol {display: none;}
#footer {
- padding: 2em 0;
- background: #222 url(../images/footer_tile.gif) repeat-x;
- }
+ padding: 2em 0;
+ background: #222 url(../images/footer_tile.gif) repeat-x;
+ }
#footer .wrapper {
- padding-left: 2em;
- width: 67em;
+ padding-left: 1em;
+ max-width: 960px;
}
-#header .wrapper, #topNav .wrapper, #feature .wrapper {padding-left: 1em; width: 68em;}
-#feature .wrapper {width: 45em; padding-right: 23em; position: relative; z-index: 0;}
+#header .wrapper, #topNav .wrapper, #feature .wrapper {padding-left: 1em; max-width: 960px;}
+#feature .wrapper {max-width: 640px; padding-right: 23em; position: relative; z-index: 0;}
+
+@media screen and (max-width: 800px) {
+ #feature .wrapper { padding-right: 0; }
+}
/* Links
--------------------------------------- */
a, a:link, a:visited {
- color: #ee3f3f;
- text-decoration: underline;
- }
+ color: #ee3f3f;
+ text-decoration: underline;
+ }
#mainCol a, #subCol a, #feature a {color: #980905;}
#mainCol a code, #subCol a code, #feature a code {color: #980905;}
@@ -162,201 +293,284 @@ a, a:link, a:visited {
/* Navigation
--------------------------------------- */
-.nav {margin: 0; padding: 0;}
-.nav li {display: inline; list-style: none;}
-
-#header .nav {
- float: right;
- margin-top: 1.5em;
- font-size: 1.2857em;
-}
-
-#header .nav li {margin: 0 0 0 0.5em;}
-#header .nav a {color: #FFF; text-decoration: none;}
-#header .nav a:hover {text-decoration: underline;}
-
-#header .nav .index {
- padding: 0.5em 1.5em;
- border-radius: 1em;
- -webkit-border-radius: 1em;
- -moz-border-radius: 1em;
- background: #980905;
- position: relative;
-}
-
-#header .nav .index a {
- background: #980905 url(../images/nav_arrow.gif) no-repeat right top;
- padding-right: 1em;
- position: relative;
- z-index: 15;
- padding-bottom: 0.125em;
+.nav {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ float: right;
+ margin-top: 1.5em;
+ font-size: 1.2857em;
+}
+
+.nav .nav-item {color: #FFF; text-decoration: none;}
+.nav .nav-item:hover {text-decoration: underline;}
+
+.guides-index-large, .guides-index-small .guides-index-item {
+ padding: 0.5em 1.5em;
+ border-radius: 1em;
+ -webkit-border-radius: 1em;
+ -moz-border-radius: 1em;
+ background: #980905;
+ position: relative;
+ color: white;
+ cursor: pointer;
+}
+
+.guides-index .guides-index-item {
+ background: #980905 url(../images/nav_arrow.gif) no-repeat right top;
+ padding-right: 1em;
+ position: relative;
+ z-index: 15;
+ padding-bottom: 0.125em;
+}
+
+.guides-index:hover .guides-index-item, .guides-index .guides-index-item:hover {
+ background-position: right -81px;
+ text-decoration: underline !important;
+}
+
+@media screen and (min-width: 481px) {
+ .nav {
+ float: right;
+ margin-top: 1.5em;
+ font-size: 1.2857em;
+ }
+ .nav>li {
+ display: inline;
+ margin-left: 0.5em;
+ }
+ .guides-index.guides-index-small {
+ display: none;
+ }
+}
+
+@media screen and (max-width: 480px) {
+
+ .nav {
+ float: none;
+ width: 100%;
+ text-align: center;
+ }
+ .nav .nav-item {
+ display: block;
+ margin: 0;
+ width: 100%;
+ background-color: #980905;
+ border: solid 1px #620c04;
+ border-top: 0;
+ padding: 15px 0;
+ text-align: center;
+ }
+ .nav .nav-item, .nav-item.guides-index-item {
+ text-transform: uppercase;
+ }
+ .nav .nav-item:first-child, .nav-item.guides-index-small {
+ border-top: solid 1px #620c04;
+ }
+ .guides-index.guides-index-small {
+ display: block;
+ margin-top: 1.5em;
+ }
+ .guides-index.guides-index-large {
+ display: none;
+ }
+ .guides-index-small .guides-index-item {
+ font: inherit;
+ padding-left: .75em;
+ font-size: .95em;
+ background-position: 96% -65px;
+ -webkit-appearance: none;
+ }
}
-#header .nav .index:hover a, #header .nav .index a:hover {background-position: right -81px;}
#guides {
- width: 27em;
- display: block;
- background: #980905;
- border-radius: 1em;
- -webkit-border-radius: 1em;
- -moz-border-radius: 1em;
- -webkit-box-shadow: 0.25em 0.25em 1em rgba(0,0,0,0.25);
- -moz-box-shadow: rgba(0,0,0,0.25) 0.25em 0.25em 1em;
- color: #f1938c;
- padding: 1.5em 2em;
- position: absolute;
- z-index: 10;
- top: -0.25em;
- right: 0;
- padding-top: 2em;
+ width: 27em;
+ display: block;
+ background: #980905;
+ border-radius: 1em;
+ -webkit-border-radius: 1em;
+ -moz-border-radius: 1em;
+ -webkit-box-shadow: 0.25em 0.25em 1em rgba(0,0,0,0.25);
+ -moz-box-shadow: rgba(0,0,0,0.25) 0.25em 0.25em 1em;
+ color: #f1938c;
+ padding: 1.5em 2em;
+ position: absolute;
+ z-index: 10;
+ top: -0.25em;
+ right: 0;
+ padding-top: 2em;
}
#guides dt, #guides dd {
- font-weight: normal;
- font-size: 0.722em;
- margin: 0;
- padding: 0;
+ font-weight: normal;
+ font-size: 0.722em;
+ margin: 0;
+ padding: 0;
}
#guides dt {padding:0; margin: 0.5em 0 0;}
#guides a {color: #FFF; background: none !important;}
#guides .L, #guides .R {float: left; width: 50%; margin: 0; padding: 0;}
#guides .R {float: right;}
#guides hr {
- display: block;
- border: none;
- height: 1px;
- color: #f1938c;
- background: #f1938c;
+ display: block;
+ border: none;
+ height: 1px;
+ color: #f1938c;
+ background: #f1938c;
}
/* Headings
--------------------------------------- */
h1 {
- font-size: 2.5em;
- line-height: 1em;
- margin: 0.6em 0 .2em;
- font-weight: bold;
- }
+ font-size: 2.5em;
+ line-height: 1em;
+ margin: 0.6em 0 .2em;
+ font-weight: bold;
+ }
h2 {
- font-size: 2.1428em;
- line-height: 1em;
- margin: 0.7em 0 .2333em;
- font-weight: bold;
- }
+ font-size: 2.1428em;
+ line-height: 1em;
+ margin: 0.7em 0 .2333em;
+ font-weight: bold;
+ }
+
+@media screen and (max-width: 480px) {
+ h2 {
+ font-size: 1.45em;
+ }
+}
h3 {
- font-size: 1.7142em;
- line-height: 1.286em;
- margin: 0.875em 0 0.2916em;
- font-weight: bold;
- }
+ font-size: 1.7142em;
+ line-height: 1.286em;
+ margin: 0.875em 0 0.2916em;
+ font-weight: bold;
+ }
+
+@media screen and (max-width: 480px) {
+ h3 {
+ font-size: 1.45em;
+ }
+}
h4 {
- font-size: 1.2857em;
- line-height: 1.2em;
- margin: 1.6667em 0 .3887em;
- font-weight: bold;
- }
+ font-size: 1.2857em;
+ line-height: 1.2em;
+ margin: 1.6667em 0 .3887em;
+ font-weight: bold;
+ }
h5 {
- font-size: 1em;
- line-height: 1.5em;
- margin: 1em 0 .5em;
- font-weight: bold;
+ font-size: 1em;
+ line-height: 1.5em;
+ margin: 1em 0 .5em;
+ font-weight: bold;
}
h6 {
- font-size: 1em;
- line-height: 1.5em;
- margin: 1em 0 .5em;
- font-weight: normal;
- }
+ font-size: 1em;
+ line-height: 1.5em;
+ margin: 1em 0 .5em;
+ font-weight: normal;
+ }
.section {
- padding-bottom: 0.25em;
- border-bottom: 1px solid #999;
+ padding-bottom: 0.25em;
+ border-bottom: 1px solid #999;
}
/* Content
--------------------------------------- */
.pic {
- margin: 0 2em 2em 0;
+ margin: 0 2em 2em 0;
}
#topNav strong {color: #999; margin-right: 0.5em;}
#topNav strong a {color: #FFF;}
#header h1 {
- float: left;
- background: url(../images/rails_guides_logo.gif) no-repeat;
- width: 297px;
- text-indent: -9999em;
- margin: 0;
- padding: 0;
+ float: left;
+ background: url(../images/rails_guides_logo.gif) no-repeat;
+ width: 297px;
+ text-indent: -9999em;
+ margin: 0;
+ padding: 0;
+}
+
+@media screen and (max-width: 480px) {
+ #header h1 {
+ float: none;
+ }
}
#header h1 a {
- text-decoration: none;
- display: block;
- height: 77px;
+ text-decoration: none;
+ display: block;
+ height: 77px;
}
#feature p {
- font-size: 1.2857em;
- margin-bottom: 0.75em;
+ font-size: 1.2857em;
+ margin-bottom: 0.75em;
+}
+
+@media screen and (max-width: 480px) {
+ #feature p {
+ font-size: 1em;
+ }
}
#feature ul {margin-left: 0;}
#feature ul li {
- list-style: none;
- background: url(../images/check_bullet.gif) no-repeat left 0.5em;
- padding: 0.5em 1.75em 0.5em 1.75em;
- font-size: 1.1428em;
- font-weight: bold;
+ list-style: none;
+ background: url(../images/check_bullet.gif) no-repeat left 0.5em;
+ padding: 0.5em 1.75em 0.5em 1.75em;
+ font-size: 1.1428em;
+ font-weight: bold;
}
#mainCol dd, #subCol dd {
- padding: 0.25em 0 1em;
- border-bottom: 1px solid #CCC;
- margin-bottom: 1em;
- margin-left: 0;
- /*padding-left: 28px;*/
- padding-left: 0;
+ padding: 0.25em 0 1em;
+ border-bottom: 1px solid #CCC;
+ margin-bottom: 1em;
+ margin-left: 0;
+ /*padding-left: 28px;*/
+ padding-left: 0;
}
#mainCol dt, #subCol dt {
- font-size: 1.2857em;
- padding: 0.125em 0 0.25em 0;
- margin-bottom: 0;
- /*background: url(../images/book_icon.gif) no-repeat left top;
- padding: 0.125em 0 0.25em 28px;*/
+ font-size: 1em;
+ padding: 0.125em 0 0.25em 0;
+ margin-bottom: 0;
+ /*background: url(../images/book_icon.gif) no-repeat left top;
+ padding: 0.125em 0 0.25em 28px;*/
}
#mainCol dd.work-in-progress, #subCol dd.work-in-progress {
- background: #fff9d8 url(../images/tab_yellow.gif) no-repeat left top;
- border: none;
- padding: 1.25em 1em 1.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #fff9d8 url(../images/tab_yellow.gif) no-repeat left top;
+ border: none;
+ padding: 1.25em 1em 1.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
#mainCol dd.kindle, #subCol dd.kindle {
- background: #d5e9f6 url(../images/tab_info.gif) no-repeat left top;
- border: none;
- padding: 1.25em 1em 1.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #d5e9f6 url(../images/tab_info.gif) no-repeat left top;
+ border: none;
+ padding: 1.25em 1em 1.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
#mainCol div.warning, #subCol dd.warning {
- background: #f9d9d8 url(../images/tab_red.gif) no-repeat left top;
- border: none;
- padding: 1.25em 1.25em 1.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #f9d9d8 url(../images/tab_red.gif) no-repeat left top;
+ border: none;
+ padding: 1.25em 1.25em 1.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
#subCol .chapters {color: #980905;}
@@ -367,31 +581,31 @@ h6 {
#subCol h3.chapter img {vertical-align: text-bottom;}
#subCol .chapters ul {margin-left: 0; margin-top: 0.5em;}
#subCol .chapters ul li {
- list-style: none;
- padding: 0 0 0 1em;
- background: url(../images/bullet.gif) no-repeat left 0.45em;
- margin-left: 0;
- font-size: 1em;
- font-weight: normal;
+ list-style: none;
+ padding: 0 0 0 1em;
+ background: url(../images/bullet.gif) no-repeat left 0.45em;
+ margin-left: 0;
+ font-size: 1em;
+ font-weight: normal;
}
div.code_container {
- background: #EEE url(../images/tab_grey.gif) no-repeat left top;
- padding: 0.25em 1em 0.5em 48px;
+ background: #EEE url(../images/tab_grey.gif) no-repeat left top;
+ padding: 0.25em 1em 0.5em 48px;
}
.note {
- background: #fff9d8 url(../images/tab_note.gif) no-repeat left top;
- border: none;
- padding: 1em 1em 0.25em 48px;
- margin: 0.25em 0 1.5em 0;
+ background: #fff9d8 url(../images/tab_note.gif) no-repeat left top;
+ border: none;
+ padding: 1em 1em 0.25em 48px;
+ margin: 0.25em 0 1.5em 0;
}
.info {
- background: #d5e9f6 url(../images/tab_info.gif) no-repeat left top;
- border: none;
- padding: 1em 1em 0.25em 48px;
- margin: 0.25em 0 1.5em 0;
+ background: #d5e9f6 url(../images/tab_info.gif) no-repeat left top;
+ border: none;
+ padding: 1em 1em 0.25em 48px;
+ margin: 0.25em 0 1.5em 0;
}
#mainCol div.todo {
@@ -404,22 +618,22 @@ div.code_container {
.note code, .info code, .todo code {border:none; background: none; padding: 0;}
#mainCol ul li {
- list-style:none;
- background: url(../images/grey_bullet.gif) no-repeat left 0.5em;
- padding-left: 1em;
- margin-left: 0;
+ list-style:none;
+ background: url(../images/grey_bullet.gif) no-repeat left 0.5em;
+ padding-left: 1em;
+ margin-left: 0;
}
#subCol .content {
- font-size: 0.7857em;
- line-height: 1.5em;
+ font-size: 0.7857em;
+ line-height: 1.5em;
}
#subCol .content li {
- font-weight: normal;
- background: none;
- padding: 0 0 1em;
- font-size: 1.1667em;
+ font-weight: normal;
+ background: none;
+ padding: 0 0 1em;
+ font-size: 1.1667em;
}
/* Clearing
@@ -460,3 +674,28 @@ div.important p, div.caution p, div.warning p, div.note p, div.info p {
z-index: 100;
border: none;
}
+
+/* Foundation v2.1.4 http://foundation.zurb.com */
+/* Artfully masterminded by ZURB */
+
+table th { font-weight: bold; }
+table td, table th { padding: 9px 10px; text-align: left; }
+
+/* Mobile */
+@media only screen and (max-width: 767px) {
+
+ table.responsive { margin-bottom: 0; }
+
+ .pinned { position: absolute; left: 0; top: 0; background: #fff; width: 35%; overflow: hidden; overflow-x: scroll; border-right: 1px solid #ccc; border-left: 1px solid #ccc; }
+ .pinned table { border-right: none; border-left: none; width: 100%; }
+ .pinned table th, .pinned table td { white-space: nowrap; }
+ .pinned td:last-child { border-bottom: 0; }
+
+ div.table-wrapper { position: relative; margin-bottom: 20px; overflow: hidden; border-right: 1px solid #ccc; }
+ div.table-wrapper div.scrollable table { margin-left: 35%; }
+ div.table-wrapper div.scrollable { overflow: scroll; overflow-y: hidden; }
+
+ table.responsive td, table.responsive th { position: relative; white-space: nowrap; overflow: hidden; }
+ table.responsive th:first-child, table.responsive td:first-child, table.responsive td:first-child, table.responsive.pinned td { display: none; }
+
+}
diff --git a/guides/assets/stylesheets/responsive-tables.css b/guides/assets/stylesheets/responsive-tables.css
new file mode 100755
index 0000000000..f5fbcbf948
--- /dev/null
+++ b/guides/assets/stylesheets/responsive-tables.css
@@ -0,0 +1,50 @@
+/* Foundation v2.1.4 http://foundation.zurb.com */
+/* Artfully masterminded by ZURB */
+
+/* --------------------------------------------------
+ Table of Contents
+-----------------------------------------------------
+:: Shared Styles
+:: Page Name 1
+:: Page Name 2
+*/
+
+
+/* -----------------------------------------
+ Shared Styles
+----------------------------------------- */
+
+table th { font-weight: bold; }
+table td, table th { padding: 9px 10px; text-align: left; }
+
+/* Mobile */
+@media only screen and (max-width: 767px) {
+
+ table { margin-bottom: 0; }
+
+ .pinned { position: absolute; left: 0; top: 0; background: #fff; width: 35%; overflow: hidden; overflow-x: scroll; border-right: 1px solid #ccc; border-left: 1px solid #ccc; }
+ .pinned table { border-right: none; border-left: none; width: 100%; }
+ .pinned table th, .pinned table td { white-space: nowrap; }
+ .pinned td:last-child { border-bottom: 0; }
+
+ div.table-wrapper { position: relative; margin-bottom: 20px; overflow: hidden; border-right: 1px solid #ccc; }
+ div.table-wrapper div.scrollable table { margin-left: 35%; }
+ div.table-wrapper div.scrollable { overflow: scroll; overflow-y: hidden; }
+
+ table td, table th { position: relative; white-space: nowrap; overflow: hidden; }
+ table th:first-child, table td:first-child, table td:first-child, table.pinned td { display: none; }
+
+}
+
+/* -----------------------------------------
+ Page Name 1
+----------------------------------------- */
+
+
+
+
+/* -----------------------------------------
+ Page Name 2
+----------------------------------------- */
+
+
diff --git a/guides/code/getting_started/app/assets/javascripts/application.js b/guides/code/getting_started/app/assets/javascripts/application.js
index 9097d830e2..93cdae76ca 100644
--- a/guides/code/getting_started/app/assets/javascripts/application.js
+++ b/guides/code/getting_started/app/assets/javascripts/application.js
@@ -5,7 +5,7 @@
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-// the compiled file.
+// compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
diff --git a/guides/rails_guides/helpers.rb b/guides/rails_guides/helpers.rb
index e6ef656474..a288d0f0f4 100644
--- a/guides/rails_guides/helpers.rb
+++ b/guides/rails_guides/helpers.rb
@@ -24,8 +24,14 @@ module RailsGuides
documents.reject { |document| document['work_in_progress'] }
end
- def docs_for_menu(position)
- position == 'L' ? documents_by_section.to(3) : documents_by_section.from(4)
+ def docs_for_menu(position=nil)
+ if position.nil?
+ documents_by_section
+ elsif position == 'L'
+ documents_by_section.to(3)
+ else
+ documents_by_section.from(4)
+ end
end
def author(name, nick, image = 'credits_pic_blank.gif', &block)
diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md
index cce5bc5331..6b3680aa2d 100644
--- a/guides/source/4_0_release_notes.md
+++ b/guides/source/4_0_release_notes.md
@@ -95,9 +95,9 @@ Railties
* Load all environments available in `config.paths["config/environments"]`.
-* Add `config.queue_consumer` to allow the default consumer to be configurable.
+* Add `config.queue_consumer` to change the job queue consumer from the default `ActiveSupport::ThreadedQueueConsumer`.
-* Add `Rails.queue` as an interface with a default implementation that consumes jobs in a separate thread.
+* Add `Rails.queue` for processing jobs in the background.
* Remove `Rack::SSL` in favour of `ActionDispatch::SSL`.
@@ -141,6 +141,8 @@ Action Mailer
end
```
+* Allow for callbacks in mailers similar to ActionController::Base. You can now set up headers/attachments using `before_filter` or `after_filter`. You could also change delivery settings or prevent delivery in an after filter based on instance variables set in your mailer action. You have access to `ActionMailer::Base` instance methods like `message`, `attachments`, `headers`.
+
Action Pack
-----------
@@ -808,6 +810,10 @@ Active Support
* Add `Time#prev_quarter` and `Time#next_quarter` short-hands for `months_ago(3)` and `months_since(3)`.
+* Add `Time#last_week`, `Time#last_month`, `Time#last_year` as aliases for `Time#prev_week`, `Time#prev_month`, and `Time#prev_year`.
+
+* Add `Date#last_week`, `Date#last_month`, `Date#last_year` as aliases for `Date#prev_week`, `Date#prev_month`, and `Date#prev_year`.
+
* Remove obsolete and unused `require_association` method from dependencies.
* Add `:instance_accessor` option for `config_accessor`.
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index e6a6b05166..824ffb5d7a 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -174,8 +174,8 @@ Your application has a session for each user in which you can store small amount
* ActionDispatch::Session::CookieStore - Stores everything on the client.
* ActionDispatch::Session::CacheStore - Stores the data in the Rails cache.
-* ActionDispatch::Session::ActiveRecordStore - Stores the data in a database using Active Record. (require `activerecord-session_store` gem).
-* ActionDispatch::Session::MemCacheStore - Stores the data in a memcached cluster (this is a legacy implementation; consider using CacheStore instead).
+* @ActionDispatch::Session::ActiveRecordStore@ - Stores the data in a database using Active Record. (require `activerecord-session_store` gem).
+* @ActionDispatch::Session::MemCacheStore@ - Stores the data in a memcached cluster (this is a legacy implementation; consider using CacheStore instead).
All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure).
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index 5e731d0a18..a938db6265 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -27,7 +27,7 @@ create app/mailers/user_mailer.rb
invoke erb
create app/views/user_mailer
invoke test_unit
-create test/functional/user_mailer_test.rb
+create test/mailers/user_mailer_test.rb
```
So we got the mailer, the views, and the tests.
@@ -476,7 +476,7 @@ The following configuration options are best made in one of the environment file
|`logger`|Generates information on the mailing run if available. Can be set to `nil` for no logging. Compatible with both Ruby's own `Logger` and `Log4r` loggers.|
|`smtp_settings`|Allows detailed configuration for `:smtp` delivery method:<ul><li>`:address` - Allows you to use a remote mail server. Just change it from its default "localhost" setting.</li><li>`:port` - On the off chance that your mail server doesn't run on port 25, you can change it.</li><li>`:domain` - If you need to specify a HELO domain, you can do it here.</li><li>`:user_name` - If your mail server requires authentication, set the username in this setting.</li><li>`:password` - If your mail server requires authentication, set the password in this setting.</li><li>`:authentication` - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of `:plain`, `:login`, `:cram_md5`.</li><li>`:enable_starttls_auto` - Set this to `false` if there is a problem with your server certificate that you cannot resolve.</li></ul>|
|`sendmail_settings`|Allows you to override options for the `:sendmail` delivery method.<ul><li>`:location` - The location of the sendmail executable. Defaults to `/usr/sbin/sendmail`.</li><li>`:arguments` - The command line arguments to be passed to sendmail. Defaults to `-i -t`.</li></ul>|
-|`raise_delivery_errors`|Whether or not errors should be raised if the email fails to be delivered.|
+|`raise_delivery_errors`|Whether or not errors should be raised if the email fails to be delivered. This only works if the external email server is configured for immediate delivery.|
|`delivery_method`|Defines a delivery method. Possible values are `:smtp` (default), `:sendmail`, `:file` and `:test`.|
|`perform_deliveries`|Determines whether deliveries are actually carried out when the `deliver` method is invoked on the Mail message. By default they are, but this can be turned off to help functional testing.|
|`deliveries`|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful for unit and functional testing.|
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 1ae6a1f204..76548a3397 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -375,8 +375,6 @@ end
Another example would be if you wanted multiple workers handling the same processing queue. You could have each worker handle 10000 records by setting the appropriate `:start` option on each worker.
-NOTE: The `:include` option allows you to name associations that should be loaded alongside with the models.
-
#### `find_in_batches`
The `find_in_batches` method is similar to `find_each`, since both retrieve batches of records. The difference is that `find_in_batches` yields _batches_ to the block as an array of models, instead of individually. The following example will yield to the supplied block an array of up to 1000 invoices at a time, with the final block containing any remaining invoices:
@@ -1227,17 +1225,17 @@ WARNING: Up to and including Rails 3.1, when the number of arguments passed to a
Find or build a new object
--------------------------
-It's common that you need to find a record or create it if it doesn't exist. You can do that with the `first_or_create` and `first_or_create!` methods.
+It's common that you need to find a record or create it if it doesn't exist. You can do that with the `find_or_create_by` and `find_or_create_by!` methods.
-### `first_or_create`
+### `find_or_create_by`
-The `first_or_create` method checks whether `first` returns `nil` or not. If it does return `nil`, then `create` is called. This is very powerful when coupled with the `where` method. Let's see an example.
+The `find_or_create_by` method checks whether a record with the attributes exists. If it doesn't, then `create` is called. Let's see an example.
-Suppose you want to find a client named 'Andy', and if there's none, create one and additionally set his `locked` attribute to false. You can do so by running:
+Suppose you want to find a client named 'Andy', and if there's none, create one. You can do so by running:
```ruby
-Client.where(:first_name => 'Andy').first_or_create(:locked => false)
-# => #<Client id: 1, first_name: "Andy", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
+Client.find_or_create_by(first_name: 'Andy')
+# => #<Client id: 1, first_name: "Andy", orders_count: 0, locked: true, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
```
The SQL generated by this method looks like this:
@@ -1245,27 +1243,39 @@ The SQL generated by this method looks like this:
```sql
SELECT * FROM clients WHERE (clients.first_name = 'Andy') LIMIT 1
BEGIN
-INSERT INTO clients (created_at, first_name, locked, orders_count, updated_at) VALUES ('2011-08-30 05:22:57', 'Andy', 0, NULL, '2011-08-30 05:22:57')
+INSERT INTO clients (created_at, first_name, locked, orders_count, updated_at) VALUES ('2011-08-30 05:22:57', 'Andy', 1, NULL, '2011-08-30 05:22:57')
COMMIT
```
-`first_or_create` returns either the record that already exists or the new record. In our case, we didn't already have a client named Andy so the record is created and returned.
+`find_or_create_by` returns either the record that already exists or the new record. In our case, we didn't already have a client named Andy so the record is created and returned.
The new record might not be saved to the database; that depends on whether validations passed or not (just like `create`).
-It's also worth noting that `first_or_create` takes into account the arguments of the `where` method. In the example above we didn't explicitly pass a `:first_name => 'Andy'` argument to `first_or_create`. However, that was used when creating the new record because it was already passed before to the `where` method.
+Suppose we want to set the 'locked' attribute to true if we're
+creating a new record, but we don't want to include it in the query. So
+we want to find the client named "Andy", or if that client doesn't
+exist, create a client named "Andy" which is not locked.
+
+We can achive this in two ways. The first is to use `create_with`:
+
+```ruby
+Client.create_with(locked: false).find_or_create_by(first_name: 'Andy')
+```
-You can do the same with the `find_or_create_by` method:
+The second way is using a block:
```ruby
-Client.find_or_create_by_first_name(:first_name => "Andy", :locked => false)
+Client.find_or_create_by(first_name: 'Andy') do |c|
+ c.locked = false
+end
```
-This method still works, but it's encouraged to use `first_or_create` because it's more explicit on which arguments are used to _find_ the record and which are used to _create_, resulting in less confusion overall.
+The block will only be executed if the client is being created. The
+second time we run this code, the block will be ignored.
-### `first_or_create!`
+### `find_or_create_by!`
-You can also use `first_or_create!` to raise an exception if the new record is invalid. Validations are not covered on this guide, but let's assume for a moment that you temporarily add
+You can also use `find_or_create_by!` to raise an exception if the new record is invalid. Validations are not covered on this guide, but let's assume for a moment that you temporarily add
```ruby
validates :orders_count, :presence => true
@@ -1274,19 +1284,21 @@ validates :orders_count, :presence => true
to your `Client` model. If you try to create a new `Client` without passing an `orders_count`, the record will be invalid and an exception will be raised:
```ruby
-Client.where(:first_name => 'Andy').first_or_create!(:locked => false)
+Client.find_or_create_by!(first_name: 'Andy')
# => ActiveRecord::RecordInvalid: Validation failed: Orders count can't be blank
```
-As with `first_or_create` there is a `find_or_create_by!` method but the `first_or_create!` method is preferred for clarity.
-
-### `first_or_initialize`
+### `find_or_initialize_by`
-The `first_or_initialize` method will work just like `first_or_create` but it will not call `create` but `new`. This means that a new model instance will be created in memory but won't be saved to the database. Continuing with the `first_or_create` example, we now want the client named 'Nick':
+The `find_or_initialize_by` method will work just like
+`find_or_create_by` but it will call `new` instead of `create`. This
+means that a new model instance will be created in memory but won't be
+saved to the database. Continuing with the `find_or_create_by` example, we
+now want the client named 'Nick':
```ruby
-nick = Client.where(:first_name => 'Nick').first_or_initialize(:locked => false)
-# => <Client id: nil, first_name: "Nick", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
+nick = Client.find_or_initialize_by(first_name: 'Nick')
+# => <Client id: nil, first_name: "Nick", orders_count: 0, locked: true, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
nick.persisted?
# => false
diff --git a/guides/source/active_record_validations_callbacks.md b/guides/source/active_record_validations_callbacks.md
index f32c1050ce..333cbdd90b 100644
--- a/guides/source/active_record_validations_callbacks.md
+++ b/guides/source/active_record_validations_callbacks.md
@@ -50,6 +50,7 @@ end
We can see how it works by looking at some `rails console` output:
```ruby
+$ rails console
>> p = Person.new(:name => "John Doe")
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil>
>> p.new_record?
@@ -60,6 +61,8 @@ We can see how it works by looking at some `rails console` output:
=> false
```
+TIP: All lines starting with a dollar sign `$` are intended to be run on the command line.
+
Creating and saving a new record will send an SQL `INSERT` operation to the database. Updating an existing record will send an SQL `UPDATE` operation instead. Validations are typically run before these commands are sent to the database. If any validations fail, the object will be marked as invalid and Active Record will not perform the `INSERT` or `UPDATE` operation. This helps to avoid storing an invalid object in the database. You can choose to have specific validations run when an object is created, saved, or updated.
CAUTION: There are many ways to change the state of an object in the database. Some methods will trigger validations, but some will not. This means that it's possible to save an object in the database in an invalid state if you aren't careful.
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 2a84242b9c..f990b4f79f 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -2867,8 +2867,16 @@ The method `extract!` removes and returns the key/value pairs matching the given
```ruby
hash = {:a => 1, :b => 2}
-rest = hash.extract!(:a) # => {:a => 1}
-hash # => {:b => 2}
+rest = hash.extract!(:a, :x) # => {:a => 1} # non-existing keys are ignored
+hash # => {:b => 2}
+```
+
+The method `extract!` returns the same subclass of Hash, that the receiver is.
+
+```ruby
+hash = {:a => 1, :b => 2}.with_indifferent_access
+rest = hash.extract!(:a).class
+# => ActiveSupport::HashWithIndifferentAccess
```
NOTE: Defined in `active_support/core_ext/hash/slice.rb`.
@@ -3097,7 +3105,8 @@ Time.local(2000, 11, 31).next_quarter # => Wed, 28 Feb 2001
The methods `beginning_of_week` and `end_of_week` return the dates for the
beginning and end of the week, respectively. Weeks are assumed to start on
-Monday, but that can be changed passing an argument.
+Monday, but that can be changed passing an argument, setting thread local
+`Date.beginning_of_week` or `config.beginning_of_week`.
```ruby
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
@@ -3111,18 +3120,24 @@ d.end_of_week(:sunday) # => Sat, 08 May 2010
##### `monday`, `sunday`
-The methods `monday` and `sunday` return the dates for the beginning and
-end of the week, respectively. Weeks are assumed to start on Monday.
+The methods `monday` and `sunday` return the dates for the previous Monday and
+next Sunday, respectively.
```ruby
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
d.monday # => Mon, 03 May 2010
d.sunday # => Sun, 09 May 2010
+
+d = Date.new(2012, 9, 10) # => Mon, 10 Sep 2012
+d.monday # => Mon, 10 Sep 2012
+
+d = Date.new(2012, 9, 16) # => Sun, 16 Sep 2012
+d.sunday # => Sun, 16 Sep 2012
```
##### `prev_week`, `next_week`
-The method `next_week` receives a symbol with a day name in English (in lowercase, default is `:monday`) and it returns the date corresponding to that day:
+The method `next_week` receives a symbol with a day name in English (default is the thread local `Date.beginning_of_week`, or `config.beginning_of_week`, or `:monday`) and it returns the date corresponding to that day.
```ruby
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
@@ -3140,6 +3155,8 @@ d.prev_week(:friday) # => Fri, 30 Apr 2010
`prev_week` is aliased to `last_week`.
+Both `next_week` and `prev_week` work as expected when `Date.beginning_of_week` or `config.beginning_of_week` are set.
+
##### `beginning_of_month`, `end_of_month`
The methods `beginning_of_month` and `end_of_month` return the dates for the beginning and end of the month:
@@ -3617,6 +3634,8 @@ now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_week
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Sun, 15 Aug 2010 23:59:59 UTC +00:00
+now.all_week(:sunday)
+# => Sun, 16 Sep 2012 00:00:00 UTC +00:00..Sat, 22 Sep 2012 23:59:59 UTC +00:00
now.all_month
# => Sat, 01 Aug 2010 00:00:00 UTC +00:00..Tue, 31 Aug 2010 23:59:59 UTC +00:00
now.all_quarter
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index dbf18b511c..42c7c07745 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -1130,6 +1130,7 @@ When you declare a `has_many` association, the declaring class automatically gai
* `collection(force_reload = false)`
* `collection<<(object, ...)`
* `collection.delete(object, ...)`
+* `collection.destroy(object, ...)`
* `collection=objects`
* `collection_singular_ids`
* `collection_singular_ids=ids`
@@ -1156,6 +1157,7 @@ Each instance of the customer model will have these methods:
orders(force_reload = false)
orders<<(object, ...)
orders.delete(object, ...)
+orders.destroy(object, ...)
orders=objects
order_ids
order_ids=ids
@@ -1195,6 +1197,15 @@ The `collection.delete` method removes one or more objects from the collection b
WARNING: Additionally, objects will be destroyed if they're associated with `:dependent => :destroy`, and deleted if they're associated with `:dependent => :delete_all`.
+##### `collection.destroy(object, ...)`
+
+The `collection.destroy` method removes one or more objects from the collection by running `destroy` on each object.
+
+```ruby
+@customer.orders.destroy(@order1)
+```
+
+WARNING: Objects will _always_ be removed from the database, ignoring the `:dependent` option.
##### `collection=objects`
@@ -1564,6 +1575,7 @@ When you declare a `has_and_belongs_to_many` association, the declaring class au
* `collection(force_reload = false)`
* `collection<<(object, ...)`
* `collection.delete(object, ...)`
+* `collection.destroy(object, ...)`
* `collection=objects`
* `collection_singular_ids`
* `collection_singular_ids=ids`
@@ -1590,6 +1602,7 @@ Each instance of the part model will have these methods:
assemblies(force_reload = false)
assemblies<<(object, ...)
assemblies.delete(object, ...)
+assemblies.destroy(object, ...)
assemblies=objects
assembly_ids
assembly_ids=ids
@@ -1636,6 +1649,16 @@ The `collection.delete` method removes one or more objects from the collection b
@part.assemblies.delete(@assembly1)
```
+WARNING: This does not trigger callbacks on the join records.
+
+##### `collection.destroy(object, ...)`
+
+The `collection.destroy` method removes one or more objects from the collection by running `destroy` on each record in the join table, including running callbacks. This does not destroy the objects.
+
+```ruby
+@part.assemblies.destroy(@assembly1)
+```
+
##### `collection=objects`
The `collection=` method makes the collection contain only the supplied objects, by adding and deleting as appropriate.
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index ce1a01d313..e4d2ecaba1 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -5,17 +5,20 @@ This guide will teach you what you need to know about avoiding that expensive ro
After reading this guide, you should be able to use and configure:
-* Page, action, and fragment caching
-* Sweepers
-* Alternative cache stores
-* Conditional GET support
+* Page, action, and fragment caching.
+* Sweepers.
+* Alternative cache stores.
+* Conditional GET support.
--------------------------------------------------------------------------------
Basic Caching
-------------
-This is an introduction to the three types of caching techniques that Rails provides by default without the use of any third party plugins.
+This is an introduction to three types of caching techniques: page, action and
+fragment caching. Rails provides by default fragment caching. In order to use
+page and action caching, you will need to add `actionpack-page_caching` and
+`actionpack-action_caching` to your Gemfile.
To start playing with caching you'll want to ensure that `config.action_controller.perform_caching` is set to `true`, if you're running in development mode. This flag is normally set in the corresponding `config/environments/*.rb` and caching is disabled by default for development and test, and enabled for production.
@@ -44,7 +47,7 @@ Let's say you have a controller called `ProductsController` and an `index` actio
By default, the page cache directory is set to `Rails.public_path` (which is usually set to the `public` folder) and this can be configured by changing the configuration setting `config.action_controller.page_cache_directory`. Changing the default from `public` helps avoid naming conflicts, since you may want to put other static html in `public`, but changing this will require web server reconfiguration to let the web server know where to serve the cached files from.
-The Page Caching mechanism will automatically add a `.html` extension to requests for pages that do not have an extension to make it easy for the webserver to find those pages and this can be configured by changing the configuration setting `config.action_controller.page_cache_extension`.
+The Page Caching mechanism will automatically add a `.html` extension to requests for pages that do not have an extension to make it easy for the webserver to find those pages and this can be configured by changing the configuration setting `config.action_controller.default_static_extension`.
In order to expire this page when a new product is added we could extend our example controller like this:
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 22645babfe..0338ef5ad0 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -134,10 +134,10 @@ Example:
`rails generate controller CreditCard open debit credit close`
Credit card controller with URLs like /credit_card/debit.
- Controller: app/controllers/credit_card_controller.rb
- Functional Test: test/functional/credit_card_controller_test.rb
- Views: app/views/credit_card/debit.html.erb [...]
- Helper: app/helpers/credit_card_helper.rb
+ Controller: app/controllers/credit_card_controller.rb
+ Test: test/controllers/credit_card_controller_test.rb
+ Views: app/views/credit_card/debit.html.erb [...]
+ Helper: app/helpers/credit_card_helper.rb
```
The controller generator is expecting parameters in the form of `generate controller ControllerName action1 action2`. Let's make a `Greetings` controller with an action of **hello**, which will say something nice to us.
@@ -150,11 +150,11 @@ $ rails generate controller Greetings hello
create app/views/greetings
create app/views/greetings/hello.html.erb
invoke test_unit
- create test/functional/greetings_controller_test.rb
+ create test/controllers/greetings_controller_test.rb
invoke helper
create app/helpers/greetings_helper.rb
invoke test_unit
- create test/unit/helpers/greetings_helper_test.rb
+ create test/helpers/greetings_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/greetings.js.coffee
@@ -223,7 +223,7 @@ $ rails generate scaffold HighScore game:string score:integer
create db/migrate/20120528060026_create_high_scores.rb
create app/models/high_score.rb
invoke test_unit
- create test/unit/high_score_test.rb
+ create test/models/high_score_test.rb
create test/fixtures/high_scores.yml
route resources :high_scores
invoke scaffold_controller
@@ -236,11 +236,11 @@ $ rails generate scaffold HighScore game:string score:integer
create app/views/high_scores/new.html.erb
create app/views/high_scores/_form.html.erb
invoke test_unit
- create test/functional/high_scores_controller_test.rb
+ create test/controllers/high_scores_controller_test.rb
invoke helper
create app/helpers/high_scores_helper.rb
invoke test_unit
- create test/unit/helpers/high_scores_helper_test.rb
+ create test/helpers/high_scores_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/high_scores.js.coffee
@@ -327,7 +327,7 @@ $ rails generate model Oops
create db/migrate/20120528062523_create_oops.rb
create app/models/oops.rb
invoke test_unit
- create test/unit/oops_test.rb
+ create test/models/oops_test.rb
create test/fixtures/oops.yml
```
```bash
@@ -336,7 +336,7 @@ $ rails destroy model Oops
remove db/migrate/20120528062523_create_oops.rb
remove app/models/oops.rb
invoke test_unit
- remove test/unit/oops_test.rb
+ remove test/models/oops_test.rb
remove test/fixtures/oops.yml
```
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 8efc1f655f..ab2766054b 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -56,14 +56,6 @@ These configuration methods are to be called on a `Rails::Railtie` object, such
* `config.asset_host` sets the host for the assets. Useful when CDNs are used for hosting assets, or when you want to work around the concurrency constraints builtin in browsers using different domain aliases. Shorter version of `config.action_controller.asset_host`.
-* `config.asset_path` lets you decorate asset paths. This can be a callable, a string, or be `nil` which is the default. For example, the normal path for `blog.js` would be `/javascripts/blog.js`, let that absolute path be `path`. If `config.asset_path` is a callable, Rails calls it when generating asset paths passing `path` as argument. If `config.asset_path` is a string, it is expected to be a `sprintf` format string with a `%s` where `path` will get inserted. In either case, Rails outputs the decorated path. Shorter version of `config.action_controller.asset_path`.
-
- ```ruby
- config.asset_path = proc { |path| "/blog/public#{path}" }
- ```
-
-NOTE. The `config.asset_path` configuration is ignored if the asset pipeline is enabled, which is the default.
-
* `config.autoload_once_paths` accepts an array of paths from which Rails will autoload constants that won't be wiped per request. Relevant if `config.cache_classes` is false, which is the case in development mode by default. Otherwise, all autoloading happens only once. All elements of this array must also be in `autoload_paths`. Default is an empty array.
* `config.autoload_paths` accepts an array of paths from which Rails will autoload constants. Default is all directories under `app`.
@@ -115,9 +107,9 @@ NOTE. The `config.asset_path` configuration is ignored if the asset pipeline is
* `config.middleware` allows you to configure the application's middleware. This is covered in depth in the [Configuring Middleware](#configuring-middleware) section below.
-* `config.queue` configures a different queue implementation for the application. Defaults to `ActiveSupport::SynchronousQueue`. Note that, if the default queue is changed, the default `queue_consumer` is not going to be initialized, it is up to the new queue implementation to handle starting and shutting down its own consumer(s).
+* `config.queue` configures the default job queue for the application. Defaults to `ActiveSupport::Queue.new` which processes jobs in a background thread. If you change the queue, you're responsible for running the jobs as well.
-* `config.queue_consumer` configures a different consumer implementation for the default queue. Defaults to `ActiveSupport::ThreadedQueueConsumer`.
+* `config.queue_consumer` configures a different job consumer for the default queue. Defaults to `ActiveSupport::ThreadedQueueConsumer`. The job consumer must respond to `start`.
* `config.reload_classes_only_on_change` enables or disables reloading of classes only when tracked files change. By default tracks everything on autoload paths and is set to true. If `config.cache_classes` is true, this option is ignored.
@@ -282,12 +274,8 @@ config.middleware.delete ActionDispatch::BestStandardsSupport
* `config.active_record.lock_optimistically` controls whether Active Record will use optimistic locking and is true by default.
-* `config.active_record.whitelist_attributes` will create an empty whitelist of attributes available for mass-assignment security for all models in your app.
-
* `config.active_record.auto_explain_threshold_in_seconds` configures the threshold for automatic EXPLAINs (`nil` disables this feature). Queries exceeding the threshold get their query plan logged. Default is 0.5 in development mode.
-* `config.active_record.mass_assignment_sanitizer` will determine the strictness of the mass assignment sanitization within Rails. Defaults to `:strict`. In this mode, mass assigning any non-`attr_accessible` attribute in a `create` or `update_attributes` call will raise an exception. Setting this option to `:logger` will only print to the log file when an attribute is being assigned and will not raise an exception.
-
The MySQL adapter adds one additional configuration option:
* `ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns in a MySQL database to be booleans and is true by default.
@@ -302,29 +290,21 @@ The schema dumper adds one additional configuration option:
* `config.action_controller.asset_host` sets the host for the assets. Useful when CDNs are used for hosting assets rather than the application server itself.
-* `config.action_controller.asset_path` takes a block which configures where assets can be found. Shorter version of `config.action_controller.asset_path`.
-
-* `config.action_controller.page_cache_directory` should be the document root for the web server and is set using `Base.page_cache_directory = "/document/root"`. For Rails, this directory has already been set to `Rails.public_path` (which is usually set to `Rails.root ` "/public"`). Changing this setting can be useful to avoid naming conflicts with files in `public/`, but doing so will likely require configuring your web server to look in the new location for cached files.
-
-* `config.action_controller.page_cache_extension` configures the extension used for cached pages saved to `page_cache_directory`. Defaults to `.html`.
-
* `config.action_controller.perform_caching` configures whether the application should perform caching or not. Set to false in development mode, true in production.
+* `config.action_controller.default_static_extension` configures the extension used for cached pages. Defaults to `.html`.
+
* `config.action_controller.default_charset` specifies the default character set for all renders. The default is "utf-8".
* `config.action_controller.logger` accepts a logger conforming to the interface of Log4r or the default Ruby Logger class, which is then used to log information from Action Controller. Set to `nil` to disable logging.
* `config.action_controller.request_forgery_protection_token` sets the token parameter name for RequestForgery. Calling `protect_from_forgery` sets it to `:authenticity_token` by default.
-* `config.action_controller.allow_forgery_protection` enables or disables CSRF protection. By default this is false in test mode and true in all other modes.
+* `config.action_controller.allow_forgery_protection` enables or disables CSRF protection. By default this is `false` in test mode and `true` in all other modes.
* `config.action_controller.relative_url_root` can be used to tell Rails that you are deploying to a subdirectory. The default is `ENV['RAILS_RELATIVE_URL_ROOT']`.
-The caching code adds two additional settings:
-
-* `ActionController::Base.page_cache_directory` sets the directory where Rails will create cached pages for your web server. The default is `Rails.public_path` (which is usually set to `Rails.root + "/public"`).
-
-* `ActionController::Base.page_cache_extension` sets the extension to be used when generating pages for the cache (this is ignored if the incoming request already has an extension). The default is `.html`.
+* `config.action_controller.permit_all_parameters` sets all the parameters for mass assignment to be permitted by default. The default value is `false`.
### Configuring Action Dispatch
@@ -587,8 +567,6 @@ Some parts of Rails can also be configured externally by supplying environment v
* `ENV["RAILS_RELATIVE_URL_ROOT"]` is used by the routing code to recognize URLs when you deploy your application to a subdirectory.
-* `ENV["RAILS_ASSET_ID"]` will override the default cache-busting timestamps that Rails generates for downloadable assets.
-
* `ENV["RAILS_CACHE_ID"]` and `ENV["RAILS_APP_VERSION"]` are used to generate expanded cache keys in Rails' caching code. This allows you to have multiple separate caches from the same application.
diff --git a/guides/source/engines.md b/guides/source/engines.md
index 6d2d8ca409..97af423f3e 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -33,10 +33,10 @@ Finally, engines would not have been possible without the work of James Adam, Pi
Generating an engine
--------------------
-To generate an engine with Rails 3.1, you will need to run the plugin generator and pass it the `--full` and `--mountable` options. To generate the beginnings of the "blorgh" engine you will need to run this command in a terminal:
+To generate an engine with Rails 3.2, you will need to run the plugin generator and pass it options as appropriate to the need. For the "blorgh" example, you will need to create a "mountable" engine, running this command in a terminal:
```bash
-$ rails plugin new blorgh --full --mountable
+$ rails plugin new blorgh --mountable
```
The full list of options for the plugin generator may be seen by typing:
@@ -45,9 +45,48 @@ The full list of options for the plugin generator may be seen by typing:
$ rails plugin --help
```
-The `--full` option tells the plugin generator that you want to create an engine, creating the basic directory structure of an engine by providing things such as an `app` directory and a `config/routes.rb` file. This generator also provides a file at `lib/blorgh/engine.rb` which is identical in function to a standard Rails application's `config/application.rb` file.
+The `--full` option tells the generator that you want to create an engine, including a skeleton structure by providing the following:
-The `--mountable` option tells the generator to mount the engine inside the dummy testing application located at `test/dummy`. It does this by placing this line into the dummy application's routes file at `test/dummy/config/routes.rb`:
+ * An `app` directory tree
+ * A `config/routes.rb` file:
+
+ ```ruby
+ Rails.application.routes.draw do
+ end
+ ```
+ * A file at `lib/blorgh/engine.rb` which is identical in function to a standard Rails application's `config/application.rb` file:
+
+ ```ruby
+ module Blorgh
+ class Engine < ::Rails::Engine
+ end
+ end
+ ```
+
+The `--mountable` option tells the generator that you want to create a "mountable" and namespace-isolated engine. This generator will provide the same skeleton structure as would the `--full` option, and will add:
+
+ * Asset manifest files (`application.js` and `application.css`)
+ * A namespaced `ApplicationController` stub
+ * A namespaced `ApplicationHelper` stub
+ * A layout view template for the engine
+ * Namespace isolation to `config/routes.rb`:
+
+ ```ruby
+ Blorgh::Engine.routes.draw do
+ end
+ ```
+
+ * Namespace isolation to `lib/blorgh/engine.rb`:
+
+ ```ruby
+ module Blorgh
+ class Engine < ::Rails::Engine
+ isolate_namespace Blorgh
+ end
+ end
+ ```
+
+Additionally, the `--mountable` option tells the generator to mount the engine inside the dummy testing application located at `test/dummy` by adding the following to the dummy application's routes file at `test/dummy/config/routes.rb`:
```ruby
mount Blorgh::Engine, :at => "blorgh"
@@ -130,7 +169,7 @@ end
This line mounts the engine at the path `/blorgh`, which will make it accessible through the application only at that path.
-Also in the test directory is the `test/integration` directory, where integration tests for the engine should be placed. Other directories can be created in the `test` directory also. For example, you may wish to create a `test/unit` directory for your unit tests.
+Also in the test directory is the `test/integration` directory, where integration tests for the engine should be placed. Other directories can be created in the `test` directory also. For example, you may wish to create a `test/models` directory for your models tests.
Providing engine functionality
------------------------------
@@ -152,7 +191,7 @@ invoke active_record
create db/migrate/[timestamp]_create_blorgh_posts.rb
create app/models/blorgh/post.rb
invoke test_unit
-create test/unit/blorgh/post_test.rb
+create test/models/blorgh/post_test.rb
create test/fixtures/blorgh/posts.yml
route resources :posts
invoke scaffold_controller
@@ -165,11 +204,11 @@ create app/views/blorgh/posts/show.html.erb
create app/views/blorgh/posts/new.html.erb
create app/views/blorgh/posts/_form.html.erb
invoke test_unit
-create test/functional/blorgh/posts_controller_test.rb
+create test/controllers/blorgh/posts_controller_test.rb
invoke helper
create app/helpers/blorgh/posts_helper.rb
invoke test_unit
-create test/unit/helpers/blorgh/posts_helper_test.rb
+create test/helpers/blorgh/posts_helper_test.rb
invoke assets
invoke js
create app/assets/javascripts/blorgh/posts.js
@@ -181,7 +220,7 @@ create app/assets/stylesheets/scaffold.css
The first thing that the scaffold generator does is invoke the `active_record` generator, which generates a migration and a model for the resource. Note here, however, that the migration is called `create_blorgh_posts` rather than the usual `create_posts`. This is due to the `isolate_namespace` method called in the `Blorgh::Engine` class's definition. The model here is also namespaced, being placed at `app/models/blorgh/post.rb` rather than `app/models/post.rb` due to the `isolate_namespace` call within the `Engine` class.
-Next, the `test_unit` generator is invoked for this model, generating a unit test at `test/unit/blorgh/post_test.rb` (rather than `test/unit/post_test.rb`) and a fixture at `test/fixtures/blorgh/posts.yml` (rather than `test/fixtures/posts.yml`).
+Next, the `test_unit` generator is invoked for this model, generating a model test at `test/models/blorgh/post_test.rb` (rather than `test/models/post_test.rb`) and a fixture at `test/fixtures/blorgh/posts.yml` (rather than `test/fixtures/posts.yml`).
After that, a line for the resource is inserted into the `config/routes.rb` file for the engine. This line is simply `resources :posts`, turning the `config/routes.rb` file for the engine into this:
@@ -193,7 +232,7 @@ end
Note here that the routes are drawn upon the `Blorgh::Engine` object rather than the `YourApp::Application` class. This is so that the engine routes are confined to the engine itself and can be mounted at a specific point as shown in the [test directory](#test-directory) section. This is also what causes the engine's routes to be isolated from those routes that are within the application. This is discussed further in the [Routes](#routes) section of this guide.
-Next, the `scaffold_controller` generator is invoked, generating a controller called `Blorgh::PostsController` (at `app/controllers/blorgh/posts_controller.rb`) and its related views at `app/views/blorgh/posts`. This generator also generates a functional test for the controller (`test/functional/blorgh/posts_controller_test.rb`) and a helper (`app/helpers/blorgh/posts_controller.rb`).
+Next, the `scaffold_controller` generator is invoked, generating a controller called `Blorgh::PostsController` (at `app/controllers/blorgh/posts_controller.rb`) and its related views at `app/views/blorgh/posts`. This generator also generates a test for the controller (`test/controllers/blorgh/posts_controller_test.rb`) and a helper (`app/helpers/blorgh/posts_controller.rb`).
Everything this generator has created is neatly namespaced. The controller's class is defined within the `Blorgh` module:
@@ -261,7 +300,7 @@ invoke active_record
create db/migrate/[timestamp]_create_blorgh_comments.rb
create app/models/blorgh/comment.rb
invoke test_unit
-create test/unit/blorgh/comment_test.rb
+create test/models/blorgh/comment_test.rb
create test/fixtures/blorgh/comments.yml
```
@@ -334,11 +373,11 @@ create app/controllers/blorgh/comments_controller.rb
invoke erb
exist app/views/blorgh/comments
invoke test_unit
-create test/functional/blorgh/comments_controller_test.rb
+create test/controllers/blorgh/comments_controller_test.rb
invoke helper
create app/helpers/blorgh/comments_helper.rb
invoke test_unit
-create test/unit/helpers/blorgh/comments_helper_test.rb
+create test/helpers/blorgh/comments_helper_test.rb
invoke assets
invoke js
create app/assets/javascripts/blorgh/comments.js
diff --git a/guides/source/generators.md b/guides/source/generators.md
index 0bcfa1dc68..d56bbe853c 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -176,7 +176,7 @@ $ rails generate scaffold User name:string
create db/migrate/20091120125558_create_users.rb
create app/models/user.rb
invoke test_unit
- create test/unit/user_test.rb
+ create test/models/user_test.rb
create test/fixtures/users.yml
route resources :users
invoke scaffold_controller
@@ -189,11 +189,11 @@ $ rails generate scaffold User name:string
create app/views/users/new.html.erb
create app/views/users/_form.html.erb
invoke test_unit
- create test/functional/users_controller_test.rb
+ create test/controllers/users_controller_test.rb
invoke helper
create app/helpers/users_helper.rb
invoke test_unit
- create test/unit/helpers/users_helper_test.rb
+ create test/helpers/users_helper_test.rb
invoke stylesheets
create app/assets/stylesheets/scaffold.css
```
@@ -350,7 +350,7 @@ $ rails generate scaffold Comment body:text
create db/migrate/20091120151323_create_comments.rb
create app/models/comment.rb
invoke shoulda
- create test/unit/comment_test.rb
+ create test/models/comment_test.rb
create test/fixtures/comments.yml
route resources :comments
invoke scaffold_controller
@@ -364,11 +364,11 @@ $ rails generate scaffold Comment body:text
create app/views/comments/_form.html.erb
create app/views/layouts/comments.html.erb
invoke shoulda
- create test/functional/comments_controller_test.rb
+ create test/controllers/comments_controller_test.rb
invoke my_helper
create app/helpers/comments_helper.rb
invoke shoulda
- create test/unit/helpers/comments_helper_test.rb
+ create test/helpers/comments_helper_test.rb
```
Fallbacks allow your generators to have a single responsibility, increasing code reuse and reducing the amount of duplication.
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index 28adad3855..9f23f9fc42 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -77,10 +77,17 @@ TIP: The examples below use # and $ to denote superuser and regular user termina
### Installing Rails
+Open up a command line prompt. On a mac this is called terminal, on windows it is called command prompt. Any commands prefaced with a dollar sign `$` should be run in the command line. Verify sure you have a current version of Ruby installed:
+
+```bash
+$ ruby -v
+ruby 1.9.3p194
+```
+
To install Rails, use the `gem install` command provided by RubyGems:
```bash
-# gem install rails
+$ gem install rails
```
TIP. A number of tools exist to help you quickly install Ruby and Ruby
@@ -154,11 +161,11 @@ $ rails server
TIP: Compiling CoffeeScript to JavaScript requires a JavaScript runtime and the absence of a runtime will give you an `execjs` error. Usually Mac OS X and Windows come with a JavaScript runtime installed. Rails adds the `therubyracer` gem to Gemfile in a commented line for new apps and you can uncomment if you need it. `therubyrhino` is the recommended runtime for JRuby users and is added by default to Gemfile in apps generated under JRuby. You can investigate about all the supported runtimes at [ExecJS](https://github.com/sstephenson/execjs#readme).
-This will fire up WEBrick, a webserver built into Ruby by default. To see your application in action, open a browser window and navigate to [http://localhost:3000](http://localhost:3000). You should see the Rails default information page:
+This will fire up WEBrick, a webserver built into Ruby by default. To see your application in action, open a browser window and navigate to <http://localhost:3000>. You should see the Rails default information page:
![Welcome Aboard screenshot](images/rails_welcome.png)
-TIP: To stop the web server, hit Ctrl+C in the terminal window where it's running. In development mode, Rails does not generally require you to restart the server; changes you make in files will be automatically picked up by the server.
+TIP: To stop the web server, hit Ctrl+C in the terminal window where it's running. To verify the server has stopped you should see your command prompt cursor again. For most unix like systems including mac this will be a dollar sign `$`. In development mode, Rails does not generally require you to restart the server; changes you make in files will be automatically picked up by the server.
The "Welcome Aboard" page is the _smoke test_ for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page. You can also click on the _About your application’s environment_ link to see a summary of your application's environment.
@@ -185,11 +192,11 @@ invoke erb
create app/views/welcome
create app/views/welcome/index.html.erb
invoke test_unit
-create test/functional/welcome_controller_test.rb
+create test/controllers/welcome_controller_test.rb
invoke helper
create app/helpers/welcome_helper.rb
invoke test_unit
-create test/unit/helpers/welcome_helper_test.rb
+create test/helpers/welcome_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/welcome.js.coffee
@@ -207,11 +214,11 @@ Open the `app/views/welcome/index.html.erb` file in your text editor and edit it
### Setting the Application Home Page
-Now that we have made the controller and view, we need to tell Rails when we want Hello Rails! to show up. In our case, we want it to show up when we navigate to the root URL of our site, [http://localhost:3000](http://localhost:3000). At the moment, however, the "Welcome Aboard" smoke test is occupying that spot.
+Now that we have made the controller and view, we need to tell Rails when we want Hello Rails! to show up. In our case, we want it to show up when we navigate to the root URL of our site, <http://localhost:3000>. At the moment, however, the "Welcome Aboard" smoke test is occupying that spot.
To fix this, delete the `index.html` file located inside the `public` directory of the application.
-You need to do this because Rails will serve any static file in the `public` directory that matches a route in preference to any dynamic content you generate from the controllers. The `index.html` file is special: it will be served if a request comes in at the root route, e.g. [http://localhost:3000](http://localhost:3000). If another request such as [http://localhost:3000/welcome](http://localhost:3000/welcome) happened, a static file at `public/welcome.html` would be served first, but only if it existed.
+You need to do this because Rails will serve any static file in the `public` directory that matches a route in preference to any dynamic content you generate from the controllers. The `index.html` file is special: it will be served if a request comes in at the root route, e.g. <http://localhost:3000>. If another request such as <http://localhost:3000/welcome> happened, a static file at `public/welcome.html` would be served first, but only if it existed.
Next, you have to tell Rails where your actual home page is located.
@@ -235,9 +242,9 @@ This is your application's _routing file_ which holds entries in a special DSL (
root :to => "welcome#index"
```
-The `root :to => "welcome#index"` tells Rails to map requests to the root of the application to the welcome controller's index action and `get "welcome/index"` tells Rails to map requests to [http://localhost:3000/welcome/index](http://localhost:3000/welcome/index) to the welcome controller's index action. This was created earlier when you ran the controller generator (`rails generate controller welcome index`).
+The `root :to => "welcome#index"` tells Rails to map requests to the root of the application to the welcome controller's index action and `get "welcome/index"` tells Rails to map requests to <http://localhost:3000/welcome/index> to the welcome controller's index action. This was created earlier when you ran the controller generator (`rails generate controller welcome index`).
-If you navigate to [http://localhost:3000](http://localhost:3000) in your browser, you'll see the `Hello, Rails!` message you put into `app/views/welcome/index.html.erb`, indicating that this new route is indeed going to `WelcomeController`'s `index` action and is rendering the view correctly.
+If you navigate to <http://localhost:3000> in your browser, you'll see the `Hello, Rails!` message you put into `app/views/welcome/index.html.erb`, indicating that this new route is indeed going to `WelcomeController`'s `index` action and is rendering the view correctly.
NOTE. For more information about routing, refer to [Rails Routing from the Outside In](routing.html).
@@ -256,7 +263,7 @@ It will look a little basic for now, but that's ok. We'll look at improving the
### Laying down the ground work
-The first thing that you are going to need to create a new post within the application is a place to do that. A great place for that would be at `/posts/new`. If you attempt to navigate to that now -- by visiting [http://localhost:3000/posts/new](http://localhost:3000/posts/new) -- Rails will give you a routing error:
+The first thing that you are going to need to create a new post within the application is a place to do that. A great place for that would be at `/posts/new`. If you attempt to navigate to that now -- by visiting <http://localhost:3000/posts/new> -- Rails will give you a routing error:
![A routing error, no route matches /posts/new](images/getting_started/routing_error_no_route_matches.png)
@@ -270,7 +277,7 @@ get "posts/new"
This route is a super-simple route: it defines a new route that only responds to `GET` requests, and that the route is at `posts/new`. But how does it know where to go without the use of the `:to` option? Well, Rails uses a sensible default here: Rails will assume that you want this route to go to the new action inside the posts controller.
-With the route defined, requests can now be made to `/posts/new` in the application. Navigate to [http://localhost:3000/posts/new](http://localhost:3000/posts/new) and you'll see another routing error:
+With the route defined, requests can now be made to `/posts/new` in the application. Navigate to <http://localhost:3000/posts/new> and you'll see another routing error:
![Another routing error, uninitialized constant PostsController](images/getting_started/routing_error_no_controller.png)
@@ -289,7 +296,7 @@ end
A controller is simply a class that is defined to inherit from `ApplicationController`. It's inside this class that you'll define methods that will become the actions for this controller. These actions will perform CRUD operations on the posts within our system.
-If you refresh [http://localhost:3000/posts/new](http://localhost:3000/posts/new) now, you'll get a new error:
+If you refresh <http://localhost:3000/posts/new> now, you'll get a new error:
![Unknown action new for PostsController!](images/getting_started/unknown_action_new_for_posts.png)
@@ -302,7 +309,7 @@ def new
end
```
-With the `new` method defined in `PostsController`, if you refresh [http://localhost:3000/posts/new](http://localhost:3000/posts/new) you'll see another error:
+With the `new` method defined in `PostsController`, if you refresh <http://localhost:3000/posts/new> you'll see another error:
![Template is missing for posts/new](images/getting_started/template_is_missing_posts_new.png)
@@ -330,7 +337,7 @@ Go ahead now and create a new file at `app/views/posts/new.html.erb` and write t
<h1>New Post</h1>
```
-When you refresh [http://localhost:3000/posts/new](http://localhost:3000/posts/new) you'll now see that the page has a title. The route, controller, action and view are now working harmoniously! It's time to create the form for a new post.
+When you refresh <http://localhost:3000/posts/new> you'll now see that the page has a title. The route, controller, action and view are now working harmoniously! It's time to create the form for a new post.
### The first form
@@ -579,7 +586,7 @@ content:
```
Finally, if you now go to
-[http://localhost:3000/posts/new](http://localhost:3000/posts/new) you'll
+<http://localhost:3000/posts/new> you'll
be able to create a post. Try it!
![Show action for posts](images/getting_started/show_action_for_posts.png)
@@ -756,7 +763,7 @@ Notice that inside the `create` action we use `render` instead of `redirect_to`
returns `false`. The `render` method is used so that the `@post` object is passed back to the `new` template when it is rendered. This rendering is done within the same request as the form submission, whereas the `redirect_to` will tell the browser to issue another request.
If you reload
-[http://localhost:3000/posts/new](http://localhost:3000/posts/new) and
+<http://localhost:3000/posts/new> and
try to save a post without a title, Rails will send you back to the
form, but that's not very useful. You need to tell the user that
something went wrong. To do that, you'll modify
@@ -1037,7 +1044,7 @@ Then do the same for the `app/views/posts/edit.html.erb` view:
<%= link_to 'Back', :action => :index %>
```
-Point your browser to [http://localhost:3000/posts/new](http://localhost:3000/posts/new) and
+Point your browser to <http://localhost:3000/posts/new> and
try creating a new post. Everything still works. Now try editing the
post and you'll receive the following error:
@@ -1057,10 +1064,10 @@ If you run `rake routes` from the console you'll see that we already
have a `posts_path` route, which was created automatically by Rails when we
defined the route for the index action.
However, we don't have a `post_path` yet, which is the reason why we
-received an error before.
+received an error before. With your server running you can view your routes by visiting [localhost:3000/rails/info/routes](http://localhost:3000/rails/info/routes), or you can generate them from the command line by running `rake routes`:
```bash
-# rake routes
+$ rake routes
posts GET /posts(.:format) posts#index
posts_new GET /posts/new(.:format) posts#new
@@ -1198,7 +1205,7 @@ If you run `rake routes`, you'll see that all the routes that we
declared before are still available:
```bash
-# rake routes
+$ rake routes
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
@@ -1239,7 +1246,7 @@ This command will generate four files:
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| db/migrate/20100207235629_create_comments.rb | Migration to create the comments table in your database (your name will include a different timestamp) |
| app/models/comment.rb | The Comment model |
-| test/unit/comment_test.rb | Unit testing harness for the comments model |
+| test/models/comment_test.rb | Testing harness for the comments model |
| test/fixtures/comments.yml | Sample comments for use in testing |
First, take a look at `comment.rb`:
@@ -1360,15 +1367,15 @@ $ rails generate controller Comments
This creates six files and one empty directory:
-| File/Directory | Purpose |
-| ------------------------------------------- | ---------------------------------------- |
-| app/controllers/comments_controller.rb | The Comments controller |
-| app/views/comments/ | Views of the controller are stored here |
-| test/functional/comments_controller_test.rb | The functional tests for the controller |
-| app/helpers/comments_helper.rb | A view helper file |
-| test/unit/helpers/comments_helper_test.rb | The unit tests for the helper |
-| app/assets/javascripts/comment.js.coffee | CoffeeScript for the controller |
-| app/assets/stylesheets/comment.css.scss | Cascading style sheet for the controller |
+| File/Directory | Purpose |
+| -------------------------------------------- | ---------------------------------------- |
+| app/controllers/comments_controller.rb | The Comments controller |
+| app/views/comments/ | Views of the controller are stored here |
+| test/controllers/comments_controller_test.rb | The test for the controller |
+| app/helpers/comments_helper.rb | A view helper file |
+| test/helpers/comments_helper_test.rb | The test for the helper |
+| app/assets/javascripts/comment.js.coffee | CoffeeScript for the controller |
+| app/assets/stylesheets/comment.css.scss | Cascading style sheet for the controller |
Like with any blog, our readers will create their comments directly after
reading the post, and once they have added their comment, will be sent back to
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index a3c6b514a4..eda1881e73 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -94,13 +94,13 @@ This means, that in the `:en` locale, the key _hello_ will map to the _Hello wor
The I18n library will use **English** as a **default locale**, i.e. if you don't set a different locale, `:en` will be used for looking up translations.
-NOTE: The i18n library takes a **pragmatic approach** to locale keys (after [some discussion](http://groups.google.com/group/rails-i18n/browse_thread/thread/14dede2c7dbe9470/80eec34395f64f3c?hl=en), including only the _locale_ ("language") part, like `:en`, `:pl`, not the _region_ part, like `:en-US` or `:en-GB`, which are traditionally used for separating "languages" and "regional setting" or "dialects". Many international applications use only the "language" element of a locale such as `:cs`, `:th` or `:es` (for Czech, Thai and Spanish). However, there are also regional differences within different language groups that may be important. For instance, in the `:en-US` locale you would have $ as a currency symbol, while in `:en-GB`, you would have £. Nothing stops you from separating regional and other settings in this way: you just have to provide full "English - United Kingdom" locale in a `:en-GB` dictionary. Various [Rails I18n plugins](http://rails-i18n.org/wiki) such as [Globalize2](ht)
+NOTE: The i18n library takes a **pragmatic approach** to locale keys (after [some discussion](http://groups.google.com/group/rails-i18n/browse_thread/thread/14dede2c7dbe9470/80eec34395f64f3c?hl=en), including only the _locale_ ("language") part, like `:en`, `:pl`, not the _region_ part, like `:en-US` or `:en-GB`, which are traditionally used for separating "languages" and "regional setting" or "dialects". Many international applications use only the "language" element of a locale such as `:cs`, `:th` or `:es` (for Czech, Thai and Spanish). However, there are also regional differences within different language groups that may be important. For instance, in the `:en-US` locale you would have $ as a currency symbol, while in `:en-GB`, you would have £. Nothing stops you from separating regional and other settings in this way: you just have to provide full "English - United Kingdom" locale in a `:en-GB` dictionary. Various [Rails I18n plugins](http://rails-i18n.org/wiki) such as [Globalize2](https://github.com/joshmh/globalize2/tree/master) may help you implement it.
The **translations load path** (`I18n.load_path`) is just a Ruby Array of paths to your translation files that will be loaded automatically and available in your application. You can pick whatever directory and translation file naming scheme makes sense for you.
NOTE: The backend will lazy-load these translations when a translation is looked up for the first time. This makes it possible to just swap the backend with something else even after translations have already been announced.
-The default `application.rb` files has instructions on how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines.
+The default initializer `locale.rb` file has instructions on how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines.
```ruby
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index c760a18dda..393bf51863 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -367,7 +367,7 @@ on your needs.
### `Rails::Server#start`
-After `congif/application` is loaded, `server.start` is called. This method is defined like this:
+After `config/application` is loaded, `server.start` is called. This method is defined like this:
```ruby
def start
@@ -534,7 +534,7 @@ require "rails"
action_controller
action_mailer
rails/test_unit
- sprockets/rails
+ sprockets
).each do |framework|
begin
require "#{framework}/railtie"
diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb
index 0a8daf7ae5..397dd62638 100644
--- a/guides/source/layout.html.erb
+++ b/guides/source/layout.html.erb
@@ -4,9 +4,9 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%= yield(:page_title) || 'Ruby on Rails Guides' %></title>
-
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print" />
@@ -25,24 +25,30 @@
<% end %>
<div id="topNav">
<div class="wrapper">
- <strong>More at <a href="http://rubyonrails.org/">rubyonrails.org:</a> </strong>
- <a href="http://rubyonrails.org/">Overview</a> |
- <a href="http://rubyonrails.org/download">Download</a> |
- <a href="http://rubyonrails.org/deploy">Deploy</a> |
- <a href="https://github.com/rails/rails">Code</a> |
- <a href="http://rubyonrails.org/screencasts">Screencasts</a> |
- <a href="http://rubyonrails.org/documentation">Documentation</a> |
- <a href="http://rubyonrails.org/ecosystem">Ecosystem</a> |
- <a href="http://rubyonrails.org/community">Community</a> |
- <a href="http://weblog.rubyonrails.org/">Blog</a>
+ <strong class="more-info-label">More at <a href="http://rubyonrails.org/">rubyonrails.org:</a> </strong>
+ <span class="red-button more-info-button">
+ More Ruby on Rails
+ </span>
+ <ul class="more-info-links s-hidden">
+ <li class="more-info"><a href="http://rubyonrails.org/">Overview</a></li>
+ <li class="more-info"><a href="http://rubyonrails.org/download">Download</a></li>
+ <li class="more-info"><a href="http://rubyonrails.org/deploy">Deploy</a></li>
+ <li class="more-info"><a href="https://github.com/rails/rails">Code</a></li>
+ <li class="more-info"><a href="http://rubyonrails.org/screencasts">Screencasts</a></li>
+ <li class="more-info"><a href="http://rubyonrails.org/documentation">Documentation</a></li>
+ <li class="more-info"><a href="http://rubyonrails.org/ecosystem">Ecosystem</a></li>
+ <li class="more-info"><a href="http://rubyonrails.org/community">Community</a></li>
+ <li class="more-info"><a href="http://weblog.rubyonrails.org/">Blog</a></li>
+ </ul>
</div>
</div>
<div id="header">
<div class="wrapper clearfix">
<h1><a href="index.html" title="Return to home page">Guides.rubyonrails.org</a></h1>
<ul class="nav">
- <li><a href="index.html">Home</a></li>
- <li class="index"><a href="index.html" onclick="guideMenu(); return false;" id="guidesMenu">Guides Index</a>
+ <li><a class="nav-item" href="index.html">Home</a></li>
+ <li class="guides-index guides-index-large">
+ <a href="index.html" onclick="guideMenu(); return false;" id="guidesMenu" class="guides-index-item nav-item">Guides Index</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
<% ['L', 'R'].each do |position| %>
@@ -57,9 +63,22 @@
<% end %>
</div>
</li>
- <li><a href="contributing_to_ruby_on_rails.html">Contribute</a></li>
- <li><a href="credits.html">Credits</a></li>
+ <li><a class="nav-item" href="contributing_to_ruby_on_rails.html">Contribute</a></li>
+ <li><a class="nav-item" href="credits.html">Credits</a></li>
+ <li class="guides-index guides-index-small">
+ <select class="guides-index-item nav-item">
+ <option value="index.html">Guides Index</option>
+ <% docs_for_menu.each do |section| %>
+ <optgroup label="<%= section['name'] %>">
+ <% finished_documents(section['documents']).each do |document| %>
+ <option value="<%= document['url'] %>"><%= document['name'] %></option>
+ <% end %>
+ </optgroup>
+ <% end %>
+ </select>
+ </li>
</ul>
+ </div>
</div>
</div>
<hr class="hide" />
@@ -113,6 +132,8 @@
</div>
</div>
+ <script type="text/javascript" src="javascripts/jquery.min.js"></script>
+ <script type="text/javascript" src="javascripts/responsive-tables.js"></script>
<script type="text/javascript" src="javascripts/guides.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shCore.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushRuby.js"></script>
@@ -121,6 +142,7 @@
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushPlain.js"></script>
<script type="text/javascript">
SyntaxHighlighter.all()
+ $(guidesIndex.bind);
</script>
</body>
</html>
diff --git a/guides/source/migrations.md b/guides/source/migrations.md
index ccbdffc9c7..57db14f30c 100644
--- a/guides/source/migrations.md
+++ b/guides/source/migrations.md
@@ -85,7 +85,7 @@ existing users.
### Using the change method
-Rails 3.1 makes migrations smarter by providing a new `change` method.
+Rails 3.1 and up makes migrations smarter by providing a `change` method.
This method is preferred for writing constructive migrations (adding columns or
tables). The migration knows how to migrate your database and reverse it when
the migration is rolled back without the need to write a separate `down` method.
@@ -235,6 +235,8 @@ adding these columns will also be created. For example, running
$ rails generate model Product name:string description:text
```
+TIP: All lines starting with a dollar sign `$` are intended to be run on the command line.
+
will create a migration that looks like this
```ruby
@@ -330,7 +332,7 @@ end
As always, what has been generated for you is just a starting point. You can add
or remove from it as you see fit by editing the
-db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb file.
+@db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb@ file.
NOTE: The generated migration file for destructive migrations will still be
old-style using the `up` and `down` methods. This is because Rails needs to know
@@ -544,7 +546,7 @@ support](#active-record-and-referential-integrity).
If the helpers provided by Active Record aren't enough you can use the `execute`
method to execute arbitrary SQL.
-For more details and examples of individual methods, check the API documentation.
+For more details and examples of individual methods, check the API documentation.
In particular the documentation for
[`ActiveRecord::ConnectionAdapters::SchemaStatements`](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html)
(which provides the methods available in the `up` and `down` methods),
@@ -700,6 +702,14 @@ will run the `up` method from the 20080906120000 migration. This task will first
check whether the migration is already performed and will do nothing if Active Record believes
that it has already been run.
+### Running Migrations in Different Environments
+
+By default running `rake db:migrate` will run in the `development` environment. To run migrations against another environment you can specify it using the `RAILS_ENV` environment variable while running the command. For example to run migrations against the `test` environment you could run:
+
+```bash
+$ rake db:migrate RAILS_ENV=test
+```
+
### Changing the Output of Running Migrations
By default migrations tell you exactly what they're doing and how long it took.
@@ -879,6 +889,27 @@ class AddFuzzToProduct < ActiveRecord::Migration
end
```
+There are other ways in which the above example could have gone badly.
+
+For example, imagine that Alice creates a migration that selectively
+updates the +description+ field on certain products. She runs the
+migration, commits the code, and then begins working on the next feature,
+which is to add a new column +fuzz+ to the products table.
+
+She creates two migrations for this new feature, one which adds the new
+column, and a second which selectively updates the +fuzz+ column based on
+other product attributes.
+
+These migrations run just fine, but when Bob comes back from his vacation
+and calls `rake db:migrate` to run all the outstanding migrations, he gets a
+subtle bug: The descriptions have defaults, and the +fuzz+ column is present,
+but +fuzz+ is nil on all products.
+
+The solution is again to use +Product.reset_column_information+ before
+referencing the Product model in a migration, ensuring the Active Record's
+knowledge of the table structure is current before manipulating data in those
+records.
+
Schema Dumping and You
----------------------
diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md
index 0e59395c8a..ba96c0c0a9 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -162,8 +162,8 @@ You can add a new middleware to the middleware stack using any of the following
config.middleware.use Rack::BounceFavicon
# Add Lifo::Cache after ActiveRecord::QueryCache.
-# Pass { :page_cache => false } argument to Lifo::Cache.
-config.middleware.insert_after ActiveRecord::QueryCache, Lifo::Cache, :page_cache => false
+# Pass { page_cache: false } argument to Lifo::Cache.
+config.middleware.insert_after ActiveRecord::QueryCache, Lifo::Cache, page_cache: false
```
#### Swapping a Middleware
diff --git a/guides/source/routing.md b/guides/source/routing.md
index 3acb7fa0e5..469fcf49fb 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -30,14 +30,14 @@ it asks the router to match it to a controller action. If the first matching rou
get "/patients/:id" => "patients#show"
```
-the request is dispatched to the `patients` controller's `show` action with `{ :id => "17" }` in `params`.
+the request is dispatched to the `patients` controller's `show` action with `{ id: "17" }` in `params`.
### Generating Paths and URLs from Code
You can also generate paths and URLs. If the route above is modified to be
```ruby
-get "/patients/:id" => "patients#show", :as => "patient"
+get "/patients/:id" => "patients#show", as: "patient"
```
If your application contains this code:
@@ -73,7 +73,7 @@ it asks the router to map it to a controller action. If the first matching route
resources :photos
```
-Rails would dispatch that request to the `destroy` method on the `photos` controller with `{ :id => "17" }` in `params`.
+Rails would dispatch that request to the `destroy` method on the `photos` controller with `{ id: "17" }` in `params`.
### CRUD, Verbs, and Actions
@@ -186,7 +186,7 @@ This will create a number of routes for each of the `posts` and `comments` contr
If you want to route `/posts` (without the prefix `/admin`) to `Admin::PostsController`, you could use
```ruby
-scope :module => "admin" do
+scope module: "admin" do
resources :posts, :comments
end
```
@@ -194,7 +194,7 @@ end
or, for a single case
```ruby
-resources :posts, :module => "admin"
+resources :posts, module: "admin"
```
If you want to route `/admin/posts` to `PostsController` (without the `Admin::` module prefix), you could use
@@ -208,7 +208,7 @@ end
or, for a single case
```ruby
-resources :posts, :path => "/admin/posts"
+resources :posts, path: "/admin/posts"
```
In each of these cases, the named routes remain the same as if you did not use `scope`. In the last case, the following paths map to `PostsController`:
@@ -375,7 +375,7 @@ Within the block of member routes, each route name specifies the HTTP verb that
```ruby
resources :photos do
- get 'preview', :on => :member
+ get 'preview', on: :member
end
```
@@ -397,7 +397,7 @@ Just as with member routes, you can pass `:on` to a route:
```ruby
resources :photos do
- get 'search', :on => :collection
+ get 'search', on: :collection
end
```
@@ -407,7 +407,7 @@ To add an alternate new action using the `:on` shortcut:
```ruby
resources :comments do
- get 'preview', :on => :new
+ get 'preview', on: :new
end
```
@@ -449,10 +449,10 @@ An incoming path of `/photos/show/1/2` will be dispatched to the `show` action o
NOTE: You can't use `:namespace` or `:module` with a `:controller` path segment. If you need to do this then use a constraint on :controller that matches the namespace you require. e.g:
```ruby
-get ':controller(/:action(/:id))', :controller => /admin\/[^\/]+/
+get ':controller(/:action(/:id))', controller: /admin\/[^\/]+/
```
-TIP: By default dynamic segments don't accept dots - this is because the dot is used as a separator for formatted routes. If you need to use a dot within a dynamic segment, add a constraint that overrides this – for example, `:id => /[^\/]+/` allows anything except a slash.
+TIP: By default dynamic segments don't accept dots - this is because the dot is used as a separator for formatted routes. If you need to use a dot within a dynamic segment, add a constraint that overrides this – for example, `id: /[^\/]+/` allows anything except a slash.
### Static Segments
@@ -462,7 +462,7 @@ You can specify static segments when creating a route:
get ':controller/:action/:id/with_user/:user_id'
```
-This route would respond to paths such as `/photos/show/1/with_user/2`. In this case, `params` would be `{ :controller => "photos", :action => "show", :id => "1", :user_id => "2" }`.
+This route would respond to paths such as `/photos/show/1/with_user/2`. In this case, `params` would be `{ controller: "photos", action: "show", id: "1", user_id: "2" }`.
### The Query String
@@ -472,7 +472,7 @@ The `params` will also include any parameters from the query string. For example
get ':controller/:action/:id'
```
-An incoming path of `/photos/show/1?user_id=2` will be dispatched to the `show` action of the `Photos` controller. `params` will be `{ :controller => "photos", :action => "show", :id => "1", :user_id => "2" }`.
+An incoming path of `/photos/show/1?user_id=2` will be dispatched to the `show` action of the `Photos` controller. `params` will be `{ controller: "photos", action: "show", id: "1", user_id: "2" }`.
### Defining Defaults
@@ -487,7 +487,7 @@ With this route, Rails will match an incoming path of `/photos/12` to the `show`
You can also define other defaults in a route by supplying a hash for the `:defaults` option. This even applies to parameters that you do not specify as dynamic segments. For example:
```ruby
-get 'photos/:id' => 'photos#show', :defaults => { :format => 'jpg' }
+get 'photos/:id' => 'photos#show', defaults: { format: 'jpg' }
```
Rails would match `photos/12` to the `show` action of `PhotosController`, and set `params[:format]` to `"jpg"`.
@@ -497,7 +497,7 @@ Rails would match `photos/12` to the `show` action of `PhotosController`, and se
You can specify a name for any route using the `:as` option.
```ruby
-get 'exit' => 'sessions#destroy', :as => :logout
+get 'exit' => 'sessions#destroy', as: :logout
```
This will create `logout_path` and `logout_url` as named helpers in your application. Calling `logout_path` will return `/exit`
@@ -505,7 +505,7 @@ This will create `logout_path` and `logout_url` as named helpers in your applica
You can also use this to override routing methods defined by resources, like this:
```ruby
-get ':username', :to => "users#show", :as => :user
+get ':username', to: "users#show", as: :user
```
This will define a `user_path` method that will be available in controllers, helpers and views that will go to a route such as `/bob`. Inside the `show` action of `UsersController`, `params[:username]` will contain the username for the user. Change `:username` in the route definition if you do not want your parameter name to be `:username`.
@@ -515,13 +515,13 @@ This will define a `user_path` method that will be available in controllers, hel
In general, you should use the `get`, `post`, `put` and `delete` methods to constrain a route to a particular verb. You can use the `match` method with the `:via` option to match multiple verbs at once:
```ruby
-match 'photos' => 'photos#show', :via => [:get, :post]
+match 'photos' => 'photos#show', via: [:get, :post]
```
-You can match all verbs to a particular route using `:via => :all`:
+You can match all verbs to a particular route using `via: :all`:
```ruby
-match 'photos' => 'photos#show', :via => :all
+match 'photos' => 'photos#show', via: :all
```
You should avoid routing all verbs to an action unless you have a good reason to, as routing both `GET` requests and `POST` requests to a single action has security implications.
@@ -531,19 +531,19 @@ You should avoid routing all verbs to an action unless you have a good reason to
You can use the `:constraints` option to enforce a format for a dynamic segment:
```ruby
-get 'photos/:id' => 'photos#show', :constraints => { :id => /[A-Z]\d{5}/ }
+get 'photos/:id' => 'photos#show', constraints: { id: /[A-Z]\d{5}/ }
```
This route would match paths such as `/photos/A12345`. You can more succinctly express the same route this way:
```ruby
-get 'photos/:id' => 'photos#show', :id => /[A-Z]\d{5}/
+get 'photos/:id' => 'photos#show', id: /[A-Z]\d{5}/
```
`:constraints` takes regular expressions with the restriction that regexp anchors can't be used. For example, the following route will not work:
```ruby
-get '/:id' => 'posts#show', :constraints => {:id => /^\d/}
+get '/:id' => 'posts#show', constraints: {id: /^\d/}
```
However, note that you don't need to use anchors because all routes are anchored at the start.
@@ -551,7 +551,7 @@ However, note that you don't need to use anchors because all routes are anchored
For example, the following routes would allow for `posts` with `to_param` values like `1-hello-world` that always begin with a number and `users` with `to_param` values like `david` that never begin with a number to share the root namespace:
```ruby
-get '/:id' => 'posts#show', :constraints => { :id => /\d.+/ }
+get '/:id' => 'posts#show', constraints: { id: /\d.+/ }
get '/:username' => 'users#show'
```
@@ -562,14 +562,14 @@ You can also constrain a route based on any method on the <a href="action_contro
You specify a request-based constraint the same way that you specify a segment constraint:
```ruby
-get "photos", :constraints => {:subdomain => "admin"}
+get "photos", constraints: {subdomain: "admin"}
```
You can also specify constraints in a block form:
```ruby
namespace :admin do
- constraints :subdomain => "admin" do
+ constraints subdomain: "admin" do
resources :photos
end
end
@@ -592,7 +592,7 @@ end
TwitterClone::Application.routes.draw do
get "*path" => "blacklist#index",
- :constraints => BlacklistConstraint.new
+ constraints: BlacklistConstraint.new
end
```
@@ -601,7 +601,7 @@ You can also specify constraints as a lambda:
```ruby
TwitterClone::Application.routes.draw do
get "*path" => "blacklist#index",
- :constraints => lambda { |request| Blacklist.retrieve_ips.include?(request.remote_ip) }
+ constraints: lambda { |request| Blacklist.retrieve_ips.include?(request.remote_ip) }
end
```
@@ -639,16 +639,16 @@ NOTE: Starting from Rails 3.1, wildcard routes will always match the optional fo
get '*pages' => 'pages#show'
```
-NOTE: By requesting `"/foo/bar.json"`, your `params[:pages]` will be equals to `"foo/bar"` with the request format of JSON. If you want the old 3.0.x behavior back, you could supply `:format => false` like this:
+NOTE: By requesting `"/foo/bar.json"`, your `params[:pages]` will be equals to `"foo/bar"` with the request format of JSON. If you want the old 3.0.x behavior back, you could supply `format: false` like this:
```ruby
-get '*pages' => 'pages#show', :format => false
+get '*pages' => 'pages#show', format: false
```
-NOTE: If you want to make the format segment mandatory, so it cannot be omitted, you can supply `:format => true` like this:
+NOTE: If you want to make the format segment mandatory, so it cannot be omitted, you can supply `format: true` like this:
```ruby
-get '*pages' => 'pages#show', :format => true
+get '*pages' => 'pages#show', format: true
```
### Redirection
@@ -681,10 +681,10 @@ In all of these cases, if you don't provide the leading host (`http://www.exampl
Instead of a String, like `"posts#index"`, which corresponds to the `index` action in the `PostsController`, you can specify any <a href="rails_on_rack.html">Rack application</a> as the endpoint for a matcher.
```ruby
-match "/application.js" => Sprockets, :via => :all
+match "/application.js" => Sprockets, via: :all
```
-As long as `Sprockets` responds to `call` and returns a `[status, headers, body]`, the router won't know the difference between the Rack application and an action. This is an appropriate use of `:via => :all`, as you will want to allow your Rack application to handle all verbs as it considers appropriate.
+As long as `Sprockets` responds to `call` and returns a `[status, headers, body]`, the router won't know the difference between the Rack application and an action. This is an appropriate use of `via: :all`, as you will want to allow your Rack application to handle all verbs as it considers appropriate.
NOTE: For the curious, `"posts#index"` actually expands out to `PostsController.action(:index)`, which returns a valid Rack application.
@@ -693,7 +693,7 @@ NOTE: For the curious, `"posts#index"` actually expands out to `PostsController.
You can specify what Rails should route `"/"` to with the `root` method:
```ruby
-root :to => 'pages#main'
+root to: 'pages#main'
root 'pages#main' # shortcut for the above
```
@@ -719,7 +719,7 @@ While the default routes and helpers generated by `resources :posts` will usuall
The `:controller` option lets you explicitly specify a controller to use for the resource. For example:
```ruby
-resources :photos, :controller => "images"
+resources :photos, controller: "images"
```
will recognize incoming paths beginning with `/photos` but route to the `Images` controller:
@@ -741,7 +741,7 @@ NOTE: Use `photos_path`, `new_photo_path`, etc. to generate paths for this resou
You can use the `:constraints` option to specify a required format on the implicit `id`. For example:
```ruby
-resources :photos, :constraints => {:id => /[A-Z][A-Z][0-9]+/}
+resources :photos, constraints: {id: /[A-Z][A-Z][0-9]+/}
```
This declaration constrains the `:id` parameter to match the supplied regular expression. So, in this case, the router would no longer match `/photos/1` to this route. Instead, `/photos/RR27` would match.
@@ -749,7 +749,7 @@ This declaration constrains the `:id` parameter to match the supplied regular ex
You can specify a single constraint to apply to a number of routes by using the block form:
```ruby
-constraints(:id => /[A-Z][A-Z][0-9]+/) do
+constraints(id: /[A-Z][A-Z][0-9]+/) do
resources :photos
resources :accounts
end
@@ -757,14 +757,14 @@ end
NOTE: Of course, you can use the more advanced constraints available in non-resourceful routes in this context.
-TIP: By default the `:id` parameter doesn't accept dots - this is because the dot is used as a separator for formatted routes. If you need to use a dot within an `:id` add a constraint which overrides this - for example `:id => /[^\/]+/` allows anything except a slash.
+TIP: By default the `:id` parameter doesn't accept dots - this is because the dot is used as a separator for formatted routes. If you need to use a dot within an `:id` add a constraint which overrides this - for example `id: /[^\/]+/` allows anything except a slash.
### Overriding the Named Helpers
The `:as` option lets you override the normal naming for the named route helpers. For example:
```ruby
-resources :photos, :as => "images"
+resources :photos, as: "images"
```
will recognize incoming paths beginning with `/photos` and route the requests to `PhotosController`, but use the value of the :as option to name the helpers.
@@ -784,7 +784,7 @@ will recognize incoming paths beginning with `/photos` and route the requests to
The `:path_names` option lets you override the automatically-generated "new" and "edit" segments in paths:
```ruby
-resources :photos, :path_names => { :new => 'make', :edit => 'change' }
+resources :photos, path_names: { new: 'make', edit: 'change' }
```
This would cause the routing to recognize paths such as
@@ -799,7 +799,7 @@ NOTE: The actual action names aren't changed by this option. The two paths shown
TIP: If you find yourself wanting to change this option uniformly for all of your routes, you can use a scope.
```ruby
-scope :path_names => { :new => "make" } do
+scope path_names: { new: "make" } do
# rest of your routes
end
```
@@ -810,7 +810,7 @@ You can use the `:as` option to prefix the named route helpers that Rails genera
```ruby
scope "admin" do
- resources :photos, :as => "admin_photos"
+ resources :photos, as: "admin_photos"
end
resources :photos
@@ -821,7 +821,7 @@ This will provide route helpers such as `admin_photos_path`, `new_admin_photo_pa
To prefix a group of route helpers, use `:as` with `scope`:
```ruby
-scope "admin", :as => "admin" do
+scope "admin", as: "admin" do
resources :photos, :accounts
end
@@ -847,7 +847,7 @@ This will provide you with URLs such as `/bob/posts/1` and will allow you to ref
By default, Rails creates routes for the seven default actions (index, show, new, create, edit, update, and destroy) for every RESTful route in your application. You can use the `:only` and `:except` options to fine-tune this behavior. The `:only` option tells Rails to create only the specified routes:
```ruby
-resources :photos, :only => [:index, :show]
+resources :photos, only: [:index, :show]
```
Now, a `GET` request to `/photos` would succeed, but a `POST` request to `/photos` (which would ordinarily be routed to the `create` action) will fail.
@@ -855,7 +855,7 @@ Now, a `GET` request to `/photos` would succeed, but a `POST` request to `/photo
The `:except` option specifies a route or list of routes that Rails should _not_ create:
```ruby
-resources :photos, :except => :destroy
+resources :photos, except: :destroy
```
In this case, Rails will create all of the normal routes except the route for `destroy` (a `DELETE` request to `/photos/:id`).
@@ -867,8 +867,8 @@ TIP: If your application has many RESTful routes, using `:only` and `:except` to
Using `scope`, we can alter path names generated by resources:
```ruby
-scope(:path_names => { :new => "neu", :edit => "bearbeiten" }) do
- resources :categories, :path => "kategorien"
+scope(path_names: { new: "neu", edit: "bearbeiten" }) do
+ resources :categories, path: "kategorien"
end
```
@@ -900,7 +900,7 @@ The `:as` option overrides the automatically-generated name for the resource in
```ruby
resources :magazines do
- resources :ads, :as => 'periodical_ads'
+ resources :ads, as: 'periodical_ads'
end
```
@@ -952,8 +952,8 @@ Routes should be included in your testing strategy (just like the rest of your a
`assert_generates` asserts that a particular set of options generate a particular path and can be used with default routes or custom routes.
```ruby
-assert_generates "/photos/1", { :controller => "photos", :action => "show", :id => "1" }
-assert_generates "/about", :controller => "pages", :action => "about"
+assert_generates "/photos/1", { controller: "photos", action: "show", id: "1" }
+assert_generates "/about", controller: "pages", action: "about"
```
#### The `assert_recognizes` Assertion
@@ -961,13 +961,13 @@ assert_generates "/about", :controller => "pages", :action => "about"
`assert_recognizes` is the inverse of `assert_generates`. It asserts that a given path is recognized and routes it to a particular spot in your application.
```ruby
-assert_recognizes({ :controller => "photos", :action => "show", :id => "1" }, "/photos/1")
+assert_recognizes({ controller: "photos", action: "show", id: "1" }, "/photos/1")
```
You can supply a `:method` argument to specify the HTTP verb:
```ruby
-assert_recognizes({ :controller => "photos", :action => "create" }, { :path => "photos", :method => :post })
+assert_recognizes({ controller: "photos", action: "create" }, { path: "photos", method: :post })
```
#### The `assert_routing` Assertion
@@ -975,5 +975,5 @@ assert_recognizes({ :controller => "photos", :action => "create" }, { :path => "
The `assert_routing` assertion checks the route both ways: it tests that the path generates the options, and that the options generate the path. Thus, it combines the functions of `assert_generates` and `assert_recognizes`.
```ruby
-assert_routing({ :path => "photos", :method => :post }, { :controller => "photos", :action => "create" })
+assert_routing({ path: "photos", method: :post }, { controller: "photos", action: "create" })
```
diff --git a/guides/source/security.md b/guides/source/security.md
index 0186386059..5ef68d2272 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -96,8 +96,8 @@ That means the security of this storage depends on this secret (and on the diges
```ruby
config.action_dispatch.session = {
- :key => '_app_session',
- :secret => '0x0dkfj3927dkc7djdh36rkckdfzsg...'
+ key: '_app_session',
+ secret: '0x0dkfj3927dkc7djdh36rkckdfzsg...'
}
```
@@ -233,7 +233,7 @@ Or the attacker places the code into the onmouseover event handler of an image:
There are many other possibilities, including Ajax to attack the victim in the background.
The _solution to this is including a security token in non-GET requests_ which check on the server-side. In Rails 2 or higher, this is a one-liner in the application controller:
```ruby
-protect_from_forgery :secret => "123456789012345678901234567890..."
+protect_from_forgery secret: "123456789012345678901234567890..."
```
This will automatically include a security token, calculated from the current session and the server-side secret, in all forms and Ajax requests generated by Rails. You won't need the secret, if you use CookieStorage as session storage. If the security token doesn't match what was expected, the session will be reset. **Note:** In Rails versions prior to 3.0.4, this raised an `ActionController::InvalidAuthenticityToken` error.
@@ -264,7 +264,7 @@ Whenever the user is allowed to pass (parts of) the URL for redirection, it is p
```ruby
def legacy
- redirect_to(params.update(:action=>'main'))
+ redirect_to(params.update(action:'main'))
end
```
@@ -334,7 +334,7 @@ basename = File.expand_path(File.join(File.dirname(__FILE__), '../../files'))
filename = File.expand_path(File.join(basename, @file.public_filename))
raise if basename !=
File.expand_path(File.join(File.dirname(filename), '../../../'))
-send_file filename, :disposition => 'inline'
+send_file filename, disposition: 'inline'
```
Another (additional) approach is to store the file names in the database and name the files on the disk after the ids in the database. This is also a good approach to avoid possible code in an uploaded file to be executed. The attachment_fu plugin does this in a similar way.
@@ -383,7 +383,7 @@ any model's attributes by manipulating the hash passed to a model's `new()` meth
```ruby
def signup
- params[:user] # => {:name=>"ow3ned", :admin=>true}
+ params[:user] # => {name:"ow3ned", admin:true}
@user = User.new(params[:user])
end
```
@@ -402,7 +402,7 @@ http://www.example.com/user/signup?user[name]=ow3ned&user[admin]=1
This will set the following parameters in the controller:
```ruby
-params[:user] # => {:name=>"ow3ned", :admin=>true}
+params[:user] # => {name:"ow3ned", admin:true}
```
So if you create a new user using mass-assignment, it may be too easy to become
@@ -459,9 +459,9 @@ should be allowed for mass updating using the slice pattern. For example:
```ruby
def signup
params[:user]
- # => {:name=>"ow3ned", :admin=>true}
+ # => {name:"ow3ned", admin:true}
permitted_params = params.require(:user).permit(:name)
- # => {:name=>"ow3ned"}
+ # => {name:"ow3ned"}
@user = User.new(permitted_params)
end
@@ -499,10 +499,11 @@ attributes.
```ruby
def user_params
- filters = [:name]
- filters << :admin if current_user.try(:admin?)
-
- params.require(:user).permit(*filters)
+ if current_user.admin?
+ params.require(:user).permit(:name, :admin)
+ else
+ params.require(:user).permit(:name)
+ end
end
```
@@ -647,7 +648,7 @@ Since this is a frequent mistake, the format validator (validates_format_of) now
```ruby
# content should include a line "Meanwhile" anywhere in the string
- validates :content, :format => { :with => /^Meanwhile$/, :multiline => true }
+ validates :content, format: { with: /^Meanwhile$/, multiline: true }
```
Note that this only protects you against the most common mistake when using the format validator - you always need to keep in mind that ^ and $ match the **line** beginning and line end in Ruby, and not the beginning and end of a string.
@@ -685,7 +686,7 @@ NOTE: _When sanitizing, protecting or verifying something, whitelists over black
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_filter :only => [...] instead of :except => [...]. This way you don't forget to turn it off for newly added actions.
+* Use before_filter only: [...] instead of except: [...]. This way you don't forget to turn it off for newly added actions.
* Use attr_accessible instead of attr_protected. See the mass-assignment section for details
* 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:
@@ -768,7 +769,7 @@ Model.where("login = ? AND password = ?", entered_user_name, entered_password).f
As you can see, the first part of the array is an SQL fragment with question marks. The sanitized versions of the variables in the second part of the array replace the question marks. Or you can pass a hash for the same result:
```ruby
-Model.where(:login => entered_user_name, :password => entered_password).first
+Model.where(login: entered_user_name, password: entered_password).first
```
The array or hash form is only available in model instances. You can try `sanitize_sql()` elsewhere. _Make it a habit to think about the security consequences when using an external string in SQL_.
@@ -863,7 +864,7 @@ This returned "some&lt;script&gt;alert('hello')&lt;/script&gt;", which makes an
```ruby
tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)
-s = sanitize(user_input, :tags => tags, :attributes => %w(href title))
+s = sanitize(user_input, tags: tags, attributes: %w(href title))
```
This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags.
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 2680525928..b45aba8d55 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -121,18 +121,18 @@ For this guide we will be using Rails _scaffolding_. It will create the model, a
NOTE: For more information on Rails <i>scaffolding</i>, refer to [Getting Started with Rails](getting_started.html)
-When you use `rails generate scaffold`, for a resource among other things it creates a test stub in the `test/unit` folder:
+When you use `rails generate scaffold`, for a resource among other things it creates a test stub in the `test/models` folder:
```bash
$ rails generate scaffold post title:string body:text
...
create app/models/post.rb
-create test/unit/post_test.rb
+create test/models/post_test.rb
create test/fixtures/posts.yml
...
```
-The default test stub in `test/unit/post_test.rb` looks like this:
+The default test stub in `test/models/post_test.rb` looks like this:
```ruby
require 'test_helper'
@@ -225,9 +225,9 @@ TIP: You can see all these rake tasks and their descriptions by running `rake --
Running a test is as simple as invoking the file containing the test cases through Ruby:
```bash
-$ ruby -Itest test/unit/post_test.rb
+$ ruby -Itest test/models/post_test.rb
-Loaded suite unit/post_test
+Loaded suite models/post_test
Started
.
Finished in 0.023513 seconds.
@@ -240,9 +240,9 @@ This will run all the test methods from the test case. Note that `test_helper.rb
You can also run a particular test method from the test case by using the `-n` switch with the `test method name`.
```bash
-$ ruby -Itest test/unit/post_test.rb -n test_the_truth
+$ ruby -Itest test/models/post_test.rb -n test_the_truth
-Loaded suite unit/post_test
+Loaded suite models/post_test
Started
.
Finished in 0.023513 seconds.
@@ -271,7 +271,7 @@ F
Finished in 0.102072 seconds.
1) Failure:
-test_should_not_save_post_without_title(PostTest) [/test/unit/post_test.rb:6]:
+test_should_not_save_post_without_title(PostTest) [/test/models/post_test.rb:6]:
<false> is not true.
1 tests, 1 assertions, 1 failures, 0 errors
@@ -290,7 +290,7 @@ Running this test shows the friendlier assertion message:
```bash
1) Failure:
-test_should_not_save_post_without_title(PostTest) [/test/unit/post_test.rb:6]:
+test_should_not_save_post_without_title(PostTest) [/test/models/post_test.rb:6]:
Saved the post without a title.
<false> is not true.
```
@@ -341,7 +341,7 @@ Finished in 0.082603 seconds.
1) Error:
test_should_report_error(PostTest):
NameError: undefined local variable or method `some_undefined_variable' for #<PostTest:0x249d354>
- /test/unit/post_test.rb:6:in `test_should_report_error'
+ /test/models/post_test.rb:6:in `test_should_report_error'
1 tests, 0 assertions, 0 failures, 1 errors
```
@@ -420,7 +420,7 @@ You should test for things such as:
* was the correct object stored in the response template?
* was the appropriate message displayed to the user in the view?
-Now that we have used Rails scaffold generator for our `Post` resource, it has already created the controller code and functional tests. You can take look at the file `posts_controller_test.rb` in the `test/functional` directory.
+Now that we have used Rails scaffold generator for our `Post` resource, it has already created the controller code and tests. You can take look at the file `posts_controller_test.rb` in the `test/controllers` directory.
Let me take you through one such test, `test_should_get_index` from the file `posts_controller_test.rb`.
@@ -762,12 +762,16 @@ You don't need to set up and run your tests by hand on a test-by-test basis. Rai
| ------------------------------- | ----------- |
| `rake test` | Runs all unit, functional and integration tests. You can also simply run `rake` as the _test_ target is the default.|
| `rake test:benchmark` | Benchmark the performance tests|
-| `rake test:functionals` | Runs all the functional tests from `test/functional`|
+| `rake test:controllers` | Runs all the controller tests from `test/controllers`|
+| `rake test:functionals` | Runs all the functional tests from `test/controllers`, `test/mailers`, and `test/functional`|
+| `rake test:helpers` | Runs all the helper tests from `test/helpers`|
| `rake test:integration` | Runs all the integration tests from `test/integration`|
+| `rake test:mailers` | Runs all the mailer tests from `test/mailers`|
+| `rake test:models` | Runs all the model tests from `test/models`|
| `rake test:profile` | Profile the performance tests|
| `rake test:recent` | Tests recent changes|
| `rake test:uncommitted` | Runs all the tests which are uncommitted. Supports Subversion and Git|
-| `rake test:units` | Runs all the unit tests from `test/unit`|
+| `rake test:units` | Runs all the unit tests from `test/models`, `test/helpers`, and `test/unit`|
Brief Note About `Test::Unit`
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index d6c425a356..d57bb1f8bc 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -69,6 +69,15 @@ in the `config/initializers/wrap_parameters.rb` file:
### Action Pack
+Rails 4.0 removed the `ActionController::Base.asset_path` option. Use the assets pipeline feature.
+
+Rails 4.0 has deprecated `ActionController::Base.page_cache_extension` option. Use
+`ActionController::Base.default_static_extension` instead.
+
+Rails 4.0 has removed Action and Page caching from ActionPack. You will need to
+add the `actionpack-action_caching` gem in order to use `caches_action` and
+the `actionpack-page_caching` to use `caches_pages` in your controllers.
+
Rails 4.0 changed how `assert_generates`, `assert_recognizes`, and `assert_routing` work. Now all these assertions raise `Assertion` instead of `ActionController::RoutingError`.
Rails 4.0 also changed the way unicode character routes are drawn. Now you can draw unicode character routes directly. If you already draw such routes, you must change them, for example:
@@ -248,7 +257,7 @@ ActiveSupport.on_load(:active_record) do
end
```
-h4(#config_session3_1). config/initializers/session_store.rb
+### config/initializers/session_store.rb
You need to change your session key to something new, or remove all sessions:
@@ -259,4 +268,6 @@ AppName::Application.config.session_store :cookie_store, :key => 'SOMETHINGNEW'
or
-<tt>$ rake db:sessions:clear</tt>
+```bash
+$ rake db:sessions:clear
+```
diff --git a/rails.gemspec b/rails.gemspec
index 3600882c5c..97f6dfeac8 100644
--- a/rails.gemspec
+++ b/rails.gemspec
@@ -25,5 +25,5 @@ Gem::Specification.new do |s|
s.add_dependency('actionmailer', version)
s.add_dependency('railties', version)
s.add_dependency('bundler', '~> 1.2')
- s.add_dependency('sprockets-rails', '~> 1.0')
+ s.add_dependency('sprockets-rails', '~> 2.0.0.rc1')
end
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 8c2b64d543..ba9b24c6c6 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,7 +1,19 @@
## Rails 4.0.0 (unreleased) ##
+* Add `.rake` to list of file extensions included by `rake notes` and `rake notes:custom`. *Brent J. Nordquist*
+
+* New test locations `test/models`, `test/helpers`, `test/controllers`, and
+ `test/mailers`. Corresponding rake tasks added as well. *Mike Moore*
+
+* Set a different cache per environment for assets pipeline
+ through `config.assets.cache`.
+
+ *Guillermo Iguaran*
+
+* `Rails.public_path` now returns a Pathname object. *Prem Sichanugrist*
+
* Remove highly uncommon `config.assets.manifest` option for moving the manifest path.
- This option is now unsupported in sprockets-rails.
+ This option is now unsupported in sprockets-rails.
*Guillermo Iguaran & Dmitry Vorotilin*
@@ -10,7 +22,7 @@
*Guillermo Iguaran*
-* Remove `config.active_record.whitelist_attributes` and
+* Remove `config.active_record.whitelist_attributes` and
`config.active_record.mass_assignment_sanitizer` from new applications since
MassAssignmentSecurity has been extracted from Rails.
@@ -69,9 +81,9 @@
* Load all environments available in `config.paths["config/environments"]`. *Piotr Sarnacki*
-* Add `config.queue_consumer` to allow the default consumer to be configurable. *Carlos Antonio da Silva*
+* Add `config.queue_consumer` to change the job queue consumer from the default `ActiveSupport::ThreadedQueueConsumer`. *Carlos Antonio da Silva*
-* Add Rails.queue as an interface with a default implementation that consumes jobs in a separate thread. *Yehuda Katz*
+* Add `Rails.queue` for processing jobs in the background. *Yehuda Katz*
* Remove Rack::SSL in favour of ActionDispatch::SSL. *Rafael Mendonça França*
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index a15965a9da..dd51a6fb01 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -106,7 +106,7 @@ module Rails
# * The environment variable RAILS_GROUPS;
# * The optional envs given as argument and the hash with group dependencies;
#
- # groups :assets => [:development, :test]
+ # groups assets: [:development, :test]
#
# # Returns
# # => [:default, :development, :assets] for Rails.env == "development"
@@ -127,7 +127,7 @@ module Rails
end
def public_path
- application && application.paths["public"].first
+ application && Pathname.new(application.paths["public"].first)
end
end
end
diff --git a/railties/lib/rails/all.rb b/railties/lib/rails/all.rb
index eabe566829..6c9c53fc69 100644
--- a/railties/lib/rails/all.rb
+++ b/railties/lib/rails/all.rb
@@ -5,7 +5,7 @@ require "rails"
action_controller
action_mailer
rails/test_unit
- sprockets/rails
+ sprockets
).each do |framework|
begin
require "#{framework}/railtie"
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 050190cba6..9ef001c7d0 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -71,7 +71,7 @@ module Rails
attr_reader :reloaders
attr_writer :queue
- delegate :default_url_options, :default_url_options=, :to => :routes
+ delegate :default_url_options, :default_url_options=, to: :routes
def initialize
super
@@ -101,6 +101,14 @@ module Rails
routes_reloader.reload!
end
+
+ # Return the application's KeyGenerator
+ def key_generator
+ # number of iterations selected based on consultation with the google security
+ # team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220
+ @key_generator ||= ActiveSupport::KeyGenerator.new(config.secret_token, iterations: 1000)
+ end
+
# Stores some of the Rails initial environment parameters which
# will be used by middlewares and engines to configure themselves.
# Currently stores:
@@ -121,7 +129,8 @@ module Rails
"action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
"action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
"action_dispatch.logger" => Rails.logger,
- "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner
+ "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner,
+ "action_dispatch.key_generator" => key_generator
})
end
@@ -189,11 +198,7 @@ module Rails
end
def queue #:nodoc:
- @queue ||= ActiveSupport::QueueContainer.new(build_queue)
- end
-
- def build_queue #:nodoc:
- config.queue.new
+ @queue ||= config.queue || ActiveSupport::Queue.new
end
def to_app #:nodoc:
@@ -281,7 +286,22 @@ module Rails
def default_middleware_stack #:nodoc:
ActionDispatch::MiddlewareStack.new.tap do |middleware|
app = self
- if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache
+ if rack_cache = config.action_dispatch.rack_cache
+ begin
+ require 'rack/cache'
+ rescue LoadError => error
+ error.message << ' Be sure to add rack-cache to your Gemfile'
+ raise
+ end
+
+ if rack_cache == true
+ rack_cache = {
+ metastore: "rails:/",
+ entitystore: "rails:/",
+ verbose: false
+ }
+ end
+
require "action_dispatch/http/rack_cache"
middleware.use ::Rack::Cache, rack_cache
end
diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb
index a1bc95550b..62d57c0cc6 100644
--- a/railties/lib/rails/application/bootstrap.rb
+++ b/railties/lib/rails/application/bootstrap.rb
@@ -7,13 +7,13 @@ module Rails
module Bootstrap
include Initializable
- initializer :load_environment_hook, :group => :all do end
+ initializer :load_environment_hook, group: :all do end
- initializer :load_active_support, :group => :all do
+ initializer :load_active_support, group: :all do
require "active_support/all" unless config.active_support.bare
end
- initializer :set_eager_load, :group => :all do
+ initializer :set_eager_load, group: :all do
if config.eager_load.nil?
warn <<-INFO
config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:
@@ -28,7 +28,7 @@ INFO
end
# Initialize the logger early in the stack in case we need to log some deprecation.
- initializer :initialize_logger, :group => :all do
+ initializer :initialize_logger, group: :all do
Rails.logger ||= config.logger || begin
path = config.paths["log"].first
unless File.exist? File.dirname path
@@ -56,7 +56,7 @@ INFO
end
# Initialize cache early in the stack so railties can make use of it.
- initializer :initialize_cache, :group => :all do
+ initializer :initialize_cache, group: :all do
unless Rails.cache
Rails.cache = ActiveSupport::Cache.lookup_store(config.cache_store)
@@ -67,11 +67,11 @@ INFO
end
# Sets the dependency loading mechanism.
- initializer :initialize_dependency_mechanism, :group => :all do
+ initializer :initialize_dependency_mechanism, group: :all do
ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
end
- initializer :bootstrap_hook, :group => :all do |app|
+ initializer :bootstrap_hook, group: :all do |app|
ActiveSupport.run_load_hooks(:before_initialize, app)
end
end
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index a7a35c2685..7df517de71 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -6,7 +6,7 @@ require 'rails/engine/configuration'
module Rails
class Application
class Configuration < ::Rails::Engine::Configuration
- attr_accessor :asset_host, :asset_path, :assets, :autoflush_log,
+ attr_accessor :asset_host, :assets, :autoflush_log,
:cache_classes, :cache_store, :consider_all_requests_local, :console,
:eager_load, :exceptions_app, :file_watcher, :filter_parameters,
:force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags,
@@ -43,14 +43,14 @@ module Rails
@exceptions_app = nil
@autoflush_log = true
@log_formatter = ActiveSupport::Logger::SimpleFormatter.new
- @queue = ActiveSupport::SynchronousQueue
- @queue_consumer = ActiveSupport::ThreadedQueueConsumer
+ @queue = ActiveSupport::SynchronousQueue.new
+ @queue_consumer = nil
@eager_load = nil
@assets = ActiveSupport::OrderedOptions.new
@assets.enabled = false
@assets.paths = []
- @assets.precompile = [ Proc.new { |path| !%w(.js .css).include?(File.extname(path)) },
+ @assets.precompile = [ Proc.new { |path, fn| fn =~ /app\/assets/ && !%w(.js .css).include?(File.extname(path)) },
/(?:\/|\\|\A)application\.(css|js)$/ ]
@assets.prefix = "/assets"
@assets.version = ''
@@ -64,10 +64,6 @@ module Rails
@assets.logger = nil
end
- def compiled_asset_path
- "/"
- end
-
def encoding=(value)
@encoding = value
silence_warnings do
@@ -79,10 +75,10 @@ module Rails
def paths
@paths ||= begin
paths = super
- paths.add "config/database", :with => "config/database.yml"
- paths.add "config/environment", :with => "config/environment.rb"
+ paths.add "config/database", with: "config/database.yml"
+ paths.add "config/environment", with: "config/environment.rb"
paths.add "lib/templates"
- paths.add "log", :with => "log/#{Rails.env}.log"
+ paths.add "log", with: "log/#{Rails.env}.log"
paths.add "public"
paths.add "public/javascripts"
paths.add "public/stylesheets"
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index d2a402aa51..c520f7af9d 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -72,7 +72,7 @@ module Rails
# Set app reload just after the finisher hook to ensure
# paths added in the hook are still loaded.
- initializer :set_clear_dependencies_hook, :group => :all do
+ initializer :set_clear_dependencies_hook, group: :all do
callback = lambda do
ActiveSupport::DescendantsTracker.clear
ActiveSupport::Dependencies.clear
@@ -83,7 +83,7 @@ module Rails
self.reloaders << reloader
# We need to set a to_prepare callback regardless of the reloader result, i.e.
# models should be reloaded if any of the reloaders (i18n, routes) were updated.
- ActionDispatch::Reloader.to_prepare(:prepend => true){ reloader.execute }
+ ActionDispatch::Reloader.to_prepare(prepend: true){ reloader.execute }
else
ActionDispatch::Reloader.to_cleanup(&callback)
end
@@ -97,8 +97,9 @@ module Rails
end
initializer :activate_queue_consumer do |app|
- if config.queue == ActiveSupport::Queue
- app.queue_consumer = config.queue_consumer.start(app.queue, {logger: Rails.logger})
+ if config.queue.class == ActiveSupport::Queue
+ app.queue_consumer = config.queue_consumer || config.queue.consumer
+ app.queue_consumer.start
at_exit { app.queue_consumer.shutdown }
end
end
diff --git a/railties/lib/rails/application/routes_reloader.rb b/railties/lib/rails/application/routes_reloader.rb
index 6f9a200aa9..737977adf9 100644
--- a/railties/lib/rails/application/routes_reloader.rb
+++ b/railties/lib/rails/application/routes_reloader.rb
@@ -4,7 +4,7 @@ module Rails
class Application
class RoutesReloader
attr_reader :route_sets, :paths
- delegate :execute_if_updated, :execute, :updated?, :to => :updater
+ delegate :execute_if_updated, :execute, :updated?, to: :updater
def initialize
@paths = []
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index 9c5dc8f188..b0fae13192 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -9,6 +9,30 @@ aliases = {
"r" => "runner"
}
+help_message = <<-EOT
+Usage: rails COMMAND [ARGS]
+
+The most common rails commands are:
+ generate Generate new code (short-cut alias: "g")
+ console Start the Rails console (short-cut alias: "c")
+ server Start the Rails server (short-cut alias: "s")
+ dbconsole Start a console for the database specified in config/database.yml
+ (short-cut alias: "db")
+ new Create a new Rails application. "rails new my_app" creates a
+ new application called MyApp in "./my_app"
+
+In addition to those, there are:
+ application Generate the Rails application code
+ destroy Undo code generated with "generate" (short-cut alias: "d")
+ benchmarker See how fast a piece of code runs
+ profiler Get profile information from a piece of code
+ plugin new Generates skeleton for developing a Rails plugin
+ runner Run a piece of code in the application environment (short-cut alias: "r")
+
+All commands can be run with -h (or --help) for more information.
+EOT
+
+
command = ARGV.shift
command = aliases[command] || command
@@ -81,29 +105,14 @@ when '--version', '-v'
ARGV.unshift '--version'
require 'rails/commands/application'
-else
- puts "Error: Command not recognized" unless %w(-h --help).include?(command)
- puts <<-EOT
-Usage: rails COMMAND [ARGS]
-
-The most common rails commands are:
- generate Generate new code (short-cut alias: "g")
- console Start the Rails console (short-cut alias: "c")
- server Start the Rails server (short-cut alias: "s")
- dbconsole Start a console for the database specified in config/database.yml
- (short-cut alias: "db")
- new Create a new Rails application. "rails new my_app" creates a
- new application called MyApp in "./my_app"
+when '-h', '--help'
+ puts help_message
-In addition to those, there are:
- application Generate the Rails application code
- destroy Undo code generated with "generate" (short-cut alias: "d")
- benchmarker See how fast a piece of code runs
- profiler Get profile information from a piece of code
- plugin new Generates skeleton for developing a Rails plugin
- runner Run a piece of code in the application environment (short-cut alias: "r")
-
-All commands can be run with -h (or --help) for more information.
- EOT
+else
+ puts "Error: Command '#{command}' not recognized"
+ if %x{rake #{command} --dry-run 2>&1 } && $?.success?
+ puts "Did you mean: `$ rake #{command}` ?\n\n"
+ end
+ puts help_message
exit(1)
end
diff --git a/railties/lib/rails/commands/application.rb b/railties/lib/rails/commands/application.rb
index 2cb6d5ca2e..ff0eda3413 100644
--- a/railties/lib/rails/commands/application.rb
+++ b/railties/lib/rails/commands/application.rb
@@ -24,7 +24,7 @@ require 'rails/generators/rails/app/app_generator'
module Rails
module Generators
- class AppGenerator
+ class AppGenerator # :nodoc:
# We want to exit on failure to be kind to other libraries
# This is only when accessing via CLI
def self.exit_on_failure?
diff --git a/railties/lib/rails/commands/destroy.rb b/railties/lib/rails/commands/destroy.rb
index 9023c61bf2..5479da86a0 100644
--- a/railties/lib/rails/commands/destroy.rb
+++ b/railties/lib/rails/commands/destroy.rb
@@ -6,4 +6,4 @@ if [nil, "-h", "--help"].include?(ARGV.first)
end
name = ARGV.shift
-Rails::Generators.invoke name, ARGV, :behavior => :revoke, :destination_root => Rails.root
+Rails::Generators.invoke name, ARGV, behavior: :revoke, destination_root: Rails.root
diff --git a/railties/lib/rails/commands/generate.rb b/railties/lib/rails/commands/generate.rb
index 9f13cb0513..351c59c645 100644
--- a/railties/lib/rails/commands/generate.rb
+++ b/railties/lib/rails/commands/generate.rb
@@ -8,4 +8,4 @@ end
name = ARGV.shift
root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
-Rails::Generators.invoke name, ARGV, :behavior => :invoke, :destination_root => root
+Rails::Generators.invoke name, ARGV, behavior: :invoke, destination_root: root
diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb
index a672258aa6..0cc672e01c 100644
--- a/railties/lib/rails/commands/runner.rb
+++ b/railties/lib/rails/commands/runner.rb
@@ -1,7 +1,7 @@
require 'optparse'
require 'rbconfig'
-options = { :environment => (ENV['RAILS_ENV'] || "development").dup }
+options = { environment: (ENV['RAILS_ENV'] || "development").dup }
code_or_file = nil
if ARGV.first.nil?
diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb
index a684129353..80fdc06cd2 100644
--- a/railties/lib/rails/commands/server.rb
+++ b/railties/lib/rails/commands/server.rb
@@ -105,13 +105,13 @@ module Rails
def default_options
super.merge({
- :Port => 3000,
- :DoNotReverseLookup => true,
- :environment => (ENV['RAILS_ENV'] || "development").dup,
- :daemonize => false,
- :debugger => false,
- :pid => File.expand_path("tmp/pids/server.pid"),
- :config => File.expand_path("config.ru")
+ Port: 3000,
+ DoNotReverseLookup: true,
+ environment: (ENV['RAILS_ENV'] || "development").dup,
+ daemonize: false,
+ debugger: false,
+ pid: File.expand_path("tmp/pids/server.pid"),
+ config: File.expand_path("config.ru")
})
end
end
diff --git a/railties/lib/rails/commands/update.rb b/railties/lib/rails/commands/update.rb
index 85a81cddf0..59fae5c337 100644
--- a/railties/lib/rails/commands/update.rb
+++ b/railties/lib/rails/commands/update.rb
@@ -6,4 +6,4 @@ if ARGV.size == 0
end
name = ARGV.shift
-Rails::Generators.invoke name, ARGV, :behavior => :skip
+Rails::Generators.invoke name, ARGV, behavior: :skip
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 7afb599910..2c2bb1c714 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -251,7 +251,7 @@ module Rails
#
# # config/routes.rb
# MyApplication::Application.routes.draw do
- # mount MyEngine::Engine => "/my_engine", :as => "my_engine"
+ # mount MyEngine::Engine => "/my_engine", as: "my_engine"
# get "/foo" => "foo#index"
# end
#
@@ -368,7 +368,7 @@ module Rails
def isolate_namespace(mod)
engine_name(generate_railtie_name(mod))
- self.routes.default_scope = { :module => ActiveSupport::Inflector.underscore(mod.name) }
+ self.routes.default_scope = { module: ActiveSupport::Inflector.underscore(mod.name) }
self.isolated = true
unless mod.respond_to?(:railtie_namespace)
@@ -407,8 +407,8 @@ module Rails
end
end
- delegate :middleware, :root, :paths, :to => :config
- delegate :engine_name, :isolated?, :to => "self.class"
+ delegate :middleware, :root, :paths, to: :config
+ delegate :engine_name, :isolated?, to: "self.class"
def initialize
@_all_autoload_paths = nil
@@ -536,7 +536,7 @@ module Rails
end
# Add configured load paths to ruby load paths and remove duplicates.
- initializer :set_load_path, :before => :bootstrap_hook do
+ initializer :set_load_path, before: :bootstrap_hook do
_all_load_paths.reverse_each do |path|
$LOAD_PATH.unshift(path) if File.directory?(path)
end
@@ -548,7 +548,7 @@ module Rails
#
# This needs to be an initializer, since it needs to run once
# per engine and get the engine as a block parameter
- initializer :set_autoload_paths, :before => :bootstrap_hook do |app|
+ initializer :set_autoload_paths, before: :bootstrap_hook do |app|
ActiveSupport::Dependencies.autoload_paths.unshift(*_all_autoload_paths)
ActiveSupport::Dependencies.autoload_once_paths.unshift(*_all_autoload_once_paths)
@@ -581,13 +581,13 @@ module Rails
end
end
- initializer :load_environment_config, :before => :load_environment_hook, :group => :all do
+ initializer :load_environment_config, before: :load_environment_hook, group: :all do
paths["config/environments"].existent.each do |environment|
require environment
end
end
- initializer :append_assets_path, :group => :all do |app|
+ initializer :append_assets_path, group: :all do |app|
app.config.assets.paths.unshift(*paths["vendor/assets"].existent_directories)
app.config.assets.paths.unshift(*paths["lib/assets"].existent_directories)
app.config.assets.paths.unshift(*paths["app/assets"].existent_directories)
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 6b18b1e249..22e885a3a6 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -20,7 +20,7 @@ module Rails
# Holds generators configuration:
#
# config.generators do |g|
- # g.orm :data_mapper, :migration => true
+ # g.orm :data_mapper, migration: true
# g.template_engine :haml
# g.test_framework :rspec
# end
@@ -38,26 +38,26 @@ module Rails
def paths
@paths ||= begin
paths = Rails::Paths::Root.new(@root)
- paths.add "app", :eager_load => true, :glob => "*"
- paths.add "app/assets", :glob => "*"
- paths.add "app/controllers", :eager_load => true
- paths.add "app/helpers", :eager_load => true
- paths.add "app/models", :eager_load => true
- paths.add "app/mailers", :eager_load => true
+ paths.add "app", eager_load: true, glob: "*"
+ paths.add "app/assets", glob: "*"
+ paths.add "app/controllers", eager_load: true
+ paths.add "app/helpers", eager_load: true
+ paths.add "app/models", eager_load: true
+ paths.add "app/mailers", eager_load: true
paths.add "app/views"
- paths.add "lib", :load_path => true
- paths.add "lib/assets", :glob => "*"
- paths.add "lib/tasks", :glob => "**/*.rake"
+ paths.add "lib", load_path: true
+ paths.add "lib/assets", glob: "*"
+ paths.add "lib/tasks", glob: "**/*.rake"
paths.add "config"
- paths.add "config/environments", :glob => "#{Rails.env}.rb"
- paths.add "config/initializers", :glob => "**/*.rb"
- paths.add "config/locales", :glob => "*.{rb,yml}"
+ paths.add "config/environments", glob: "#{Rails.env}.rb"
+ paths.add "config/initializers", glob: "**/*.rb"
+ paths.add "config/locales", glob: "*.{rb,yml}"
paths.add "config/routes.rb"
paths.add "db"
paths.add "db/migrate"
paths.add "db/seeds.rb"
- paths.add "vendor", :load_path => true
- paths.add "vendor/assets", :glob => "*"
+ paths.add "vendor", load_path: true
+ paths.add "vendor/assets", glob: "*"
paths
end
end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index a8c0626a41..367f9288b8 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -23,41 +23,41 @@ module Rails
mattr_accessor :namespace
DEFAULT_ALIASES = {
- :rails => {
- :actions => '-a',
- :orm => '-o',
- :javascripts => '-j',
- :javascript_engine => '-je',
- :resource_controller => '-c',
- :scaffold_controller => '-c',
- :stylesheets => '-y',
- :stylesheet_engine => '-se',
- :template_engine => '-e',
- :test_framework => '-t'
+ rails: {
+ actions: '-a',
+ orm: '-o',
+ javascripts: '-j',
+ javascript_engine: '-je',
+ resource_controller: '-c',
+ scaffold_controller: '-c',
+ stylesheets: '-y',
+ stylesheet_engine: '-se',
+ template_engine: '-e',
+ test_framework: '-t'
},
- :test_unit => {
- :fixture_replacement => '-r',
+ test_unit: {
+ fixture_replacement: '-r',
}
}
DEFAULT_OPTIONS = {
- :rails => {
- :assets => true,
- :force_plural => false,
- :helper => true,
- :integration_tool => nil,
- :javascripts => true,
- :javascript_engine => :js,
- :orm => false,
- :performance_tool => nil,
- :resource_controller => :controller,
- :resource_route => true,
- :scaffold_controller => :scaffold_controller,
- :stylesheets => true,
- :stylesheet_engine => :css,
- :test_framework => false,
- :template_engine => :erb
+ rails: {
+ assets: true,
+ force_plural: false,
+ helper: true,
+ integration_tool: nil,
+ javascripts: true,
+ javascript_engine: :js,
+ orm: false,
+ performance_tool: nil,
+ resource_controller: :controller,
+ resource_route: true,
+ scaffold_controller: :scaffold_controller,
+ stylesheets: true,
+ stylesheet_engine: :css,
+ test_framework: false,
+ template_engine: :erb
}
}
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index c41acc7841..5c4e81431c 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -7,9 +7,9 @@ module Rails
# Adds an entry into Gemfile for the supplied gem.
#
- # gem "rspec", :group => :test
- # gem "technoweenie-restful-authentication", :lib => "restful-authentication", :source => "http://gems.github.com/"
- # gem "rails", "3.0", :git => "git://github.com/rails/rails"
+ # gem "rspec", group: :test
+ # gem "technoweenie-restful-authentication", lib: "restful-authentication", source: "http://gems.github.com/"
+ # gem "rails", "3.0", git: "git://github.com/rails/rails"
def gem(*args)
options = args.extract_options!
name, version = args
@@ -33,7 +33,7 @@ module Rails
str = "gem #{parts.join(", ")}"
str = " " + str if @in_group
str = "\n" + str
- append_file "Gemfile", str, :verbose => false
+ append_file "Gemfile", str, verbose: false
end
end
@@ -47,13 +47,13 @@ module Rails
log :gemfile, "group #{name}"
in_root do
- append_file "Gemfile", "\ngroup #{name} do", :force => true
+ append_file "Gemfile", "\ngroup #{name} do", force: true
@in_group = true
instance_eval(&block)
@in_group = false
- append_file "Gemfile", "\nend\n", :force => true
+ append_file "Gemfile", "\nend\n", force: true
end
end
@@ -64,7 +64,7 @@ module Rails
log :source, source
in_root do
- prepend_file "Gemfile", "source #{source.inspect}\n", :verbose => false
+ prepend_file "Gemfile", "source #{source.inspect}\n", verbose: false
end
end
@@ -77,7 +77,7 @@ module Rails
# "config.autoload_paths += %W(#{config.root}/extras)"
# end
#
- # environment(nil, :env => "development") do
+ # environment(nil, env: "development") do
# "config.active_record.observers = :cacher"
# end
def environment(data=nil, options={}, &block)
@@ -87,10 +87,10 @@ module Rails
in_root do
if options[:env].nil?
- inject_into_file 'config/application.rb', "\n #{data}", :after => sentinel, :verbose => false
+ inject_into_file 'config/application.rb', "\n #{data}", after: sentinel, verbose: false
else
Array(options[:env]).each do |env|
- inject_into_file "config/environments/#{env}.rb", "\n #{data}", :after => env_file_sentinel, :verbose => false
+ inject_into_file "config/environments/#{env}.rb", "\n #{data}", after: env_file_sentinel, verbose: false
end
end
end
@@ -100,8 +100,8 @@ module Rails
# Run a command in git.
#
# git :init
- # git :add => "this.file that.rb"
- # git :add => "onefile.rb", :rm => "badfile.cxx"
+ # git add: "this.file that.rb"
+ # git add: "onefile.rb", rm: "badfile.cxx"
def git(commands={})
if commands.is_a?(Symbol)
run "git #{commands}"
@@ -123,7 +123,7 @@ module Rails
# vendor("foreign.rb", "# Foreign code is fun")
def vendor(filename, data=nil, &block)
log :vendor, filename
- create_file("vendor/#{filename}", data, :verbose => false, &block)
+ create_file("vendor/#{filename}", data, verbose: false, &block)
end
# Create a new file in the lib/ directory. Code can be specified
@@ -136,7 +136,7 @@ module Rails
# lib("foreign.rb", "# Foreign code is fun")
def lib(filename, data=nil, &block)
log :lib, filename
- create_file("lib/#{filename}", data, :verbose => false, &block)
+ create_file("lib/#{filename}", data, verbose: false, &block)
end
# Create a new Rakefile with the provided code (either in a block or a string).
@@ -156,7 +156,7 @@ module Rails
# rakefile('seed.rake', 'puts "Planting seeds"')
def rakefile(filename, data=nil, &block)
log :rakefile, filename
- create_file("lib/tasks/#{filename}", data, :verbose => false, &block)
+ create_file("lib/tasks/#{filename}", data, verbose: false, &block)
end
# Create a new initializer with the provided code (either in a block or a string).
@@ -174,7 +174,7 @@ module Rails
# initializer("api.rb", "API_KEY = '123456'")
def initializer(filename, data=nil, &block)
log :initializer, filename
- create_file("config/initializers/#{filename}", data, :verbose => false, &block)
+ create_file("config/initializers/#{filename}", data, verbose: false, &block)
end
# Generate something using a generator from Rails or a plugin.
@@ -186,19 +186,19 @@ module Rails
log :generate, what
argument = args.map {|arg| arg.to_s }.flatten.join(" ")
- in_root { run_ruby_script("script/rails generate #{what} #{argument}", :verbose => false) }
+ in_root { run_ruby_script("script/rails generate #{what} #{argument}", verbose: false) }
end
# Runs the supplied rake task
#
# rake("db:migrate")
- # rake("db:migrate", :env => "production")
- # rake("gems:install", :sudo => true)
+ # rake("db:migrate", env: "production")
+ # rake("gems:install", sudo: true)
def rake(command, options={})
log :rake, command
env = options[:env] || ENV["RAILS_ENV"] || 'development'
sudo = options[:sudo] && RbConfig::CONFIG['host_os'] !~ /mswin|mingw/ ? 'sudo ' : ''
- in_root { run("#{sudo}#{extify(:rake)} #{command} RAILS_ENV=#{env}", :verbose => false) }
+ in_root { run("#{sudo}#{extify(:rake)} #{command} RAILS_ENV=#{env}", verbose: false) }
end
# Just run the capify command in root
@@ -206,7 +206,7 @@ module Rails
# capify!
def capify!
log :capify, ""
- in_root { run("#{extify(:capify)} .", :verbose => false) }
+ in_root { run("#{extify(:capify)} .", verbose: false) }
end
# Make an entry in Rails routing file config/routes.rb
@@ -217,7 +217,7 @@ module Rails
sentinel = /\.routes\.draw do\s*$/
in_root do
- inject_into_file 'config/routes.rb', "\n #{routing_code}", { :after => sentinel, :verbose => false }
+ inject_into_file 'config/routes.rb', "\n #{routing_code}", { after: sentinel, verbose: false }
end
end
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 184c59cb90..e761e26b04 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -8,7 +8,7 @@ require 'uri'
module Rails
module Generators
- class AppBase < Base
+ class AppBase < Base # :nodoc:
DATABASES = %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver )
JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc )
DATABASES.concat(JDBC_DATABASES)
@@ -154,10 +154,8 @@ module Rails
GEMFILE
else
<<-GEMFILE.strip_heredoc
+ # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '#{Rails::VERSION::STRING}'
-
- # Bundle edge Rails instead:
- # gem 'rails', github: 'rails/rails'
GEMFILE
end
end
@@ -226,7 +224,14 @@ module Rails
end
def javascript_gemfile_entry
- "gem '#{options[:javascript]}-rails'" unless options[:skip_javascript]
+ unless options[:skip_javascript]
+ <<-GEMFILE.strip_heredoc
+ gem '#{options[:javascript]}-rails'
+
+ # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
+ gem 'turbolinks'
+ GEMFILE
+ end
end
def javascript_runtime_gemfile_entry
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index c2accf0387..7e938fab47 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -11,7 +11,7 @@ require 'rails/generators/actions'
module Rails
module Generators
- class Error < Thor::Error
+ class Error < Thor::Error # :nodoc:
end
class Base < Thor::Group
@@ -61,7 +61,7 @@ module Rails
#
# module Rails::Generators
# class ControllerGenerator < Base
- # hook_for :test_framework, :aliases => "-t"
+ # hook_for :test_framework, aliases: "-t"
# end
# end
#
@@ -102,7 +102,7 @@ module Rails
# :as option:
#
# class AwesomeGenerator < Rails::Generators::Base
- # hook_for :test_framework, :as => :controller
+ # hook_for :test_framework, as: :controller
# end
#
# And now it will lookup at:
@@ -113,7 +113,7 @@ module Rails
# need to provide the :base value:
#
# class AwesomeGenerator < Rails::Generators::Base
- # hook_for :test_framework, :in => :rails, :as => :controller
+ # hook_for :test_framework, in: :rails, as: :controller
# end
#
# And the lookup is exactly the same as previously:
@@ -137,7 +137,7 @@ module Rails
# developers might want to have webrat available on controller generator.
# This can be achieved as:
#
- # Rails::Generators::ControllerGenerator.hook_for :webrat, :type => :boolean
+ # Rails::Generators::ControllerGenerator.hook_for :webrat, type: :boolean
#
# Then, if you want webrat to be invoked, just supply:
#
@@ -171,9 +171,9 @@ module Rails
defaults = if options[:type] == :boolean
{ }
elsif [true, false].include?(default_value_for_option(name, options))
- { :banner => "" }
+ { banner: "" }
else
- { :desc => "#{name.to_s.humanize} to be invoked", :banner => "NAME" }
+ { desc: "#{name.to_s.humanize} to be invoked", banner: "NAME" }
end
unless class_options.key?(name)
@@ -343,8 +343,8 @@ module Rails
# Small macro to add ruby as an option to the generator with proper
# default value plus an instance helper method called shebang.
def self.add_shebang_option!
- class_option :ruby, :type => :string, :aliases => "-r", :default => Thor::Util.ruby_command,
- :desc => "Path to the Ruby binary of your choice", :banner => "PATH"
+ class_option :ruby, type: :string, aliases: "-r", default: Thor::Util.ruby_command,
+ desc: "Path to the Ruby binary of your choice", banner: "PATH"
no_tasks {
define_method :shebang do
diff --git a/railties/lib/rails/generators/css/assets/assets_generator.rb b/railties/lib/rails/generators/css/assets/assets_generator.rb
index 492177ca2e..e4a305f4b3 100644
--- a/railties/lib/rails/generators/css/assets/assets_generator.rb
+++ b/railties/lib/rails/generators/css/assets/assets_generator.rb
@@ -1,8 +1,8 @@
require "rails/generators/named_base"
-module Css
- module Generators
- class AssetsGenerator < Rails::Generators::NamedBase
+module Css # :nodoc:
+ module Generators # :nodoc:
+ class AssetsGenerator < Rails::Generators::NamedBase # :nodoc:
source_root File.expand_path("../templates", __FILE__)
def copy_stylesheet
diff --git a/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb
index 1d7fe9fac0..cf534030f9 100644
--- a/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/css/scaffold/scaffold_generator.rb
@@ -1,8 +1,8 @@
require "rails/generators/named_base"
-module Css
- module Generators
- class ScaffoldGenerator < Rails::Generators::NamedBase
+module Css # :nodoc:
+ module Generators # :nodoc:
+ class ScaffoldGenerator < Rails::Generators::NamedBase # :nodoc:
# In order to allow the Sass generators to pick up the default Rails CSS and
# transform it, we leave it in a standard location for the CSS stylesheet
# generators to handle. For the simple, default case, just copy it over.
diff --git a/railties/lib/rails/generators/erb.rb b/railties/lib/rails/generators/erb.rb
index 3e6371268f..73e986ee7f 100644
--- a/railties/lib/rails/generators/erb.rb
+++ b/railties/lib/rails/generators/erb.rb
@@ -1,7 +1,7 @@
require 'rails/generators/named_base'
-module Erb
- module Generators
+module Erb # :nodoc:
+ module Generators # :nodoc:
class Base < Rails::Generators::NamedBase #:nodoc:
protected
diff --git a/railties/lib/rails/generators/erb/controller/controller_generator.rb b/railties/lib/rails/generators/erb/controller/controller_generator.rb
index ac57140c23..5f06734ab8 100644
--- a/railties/lib/rails/generators/erb/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/erb/controller/controller_generator.rb
@@ -1,9 +1,9 @@
require 'rails/generators/erb'
-module Erb
- module Generators
- class ControllerGenerator < Base
- argument :actions, :type => :array, :default => [], :banner => "action action"
+module Erb # :nodoc:
+ module Generators # :nodoc:
+ class ControllerGenerator < Base # :nodoc:
+ argument :actions, type: :array, default: [], banner: "action action"
def copy_view_files
base_path = File.join("app/views", class_path, file_name)
diff --git a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
index 943d0c9f8d..7bcac30dde 100644
--- a/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
+++ b/railties/lib/rails/generators/erb/mailer/mailer_generator.rb
@@ -1,8 +1,8 @@
require 'rails/generators/erb/controller/controller_generator'
-module Erb
- module Generators
- class MailerGenerator < ControllerGenerator
+module Erb # :nodoc:
+ module Generators # :nodoc:
+ class MailerGenerator < ControllerGenerator # :nodoc:
protected
def format
diff --git a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
index b2c8d7051b..bacbc2d280 100644
--- a/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/erb/scaffold/scaffold_generator.rb
@@ -1,12 +1,12 @@
require 'rails/generators/erb'
require 'rails/generators/resource_helpers'
-module Erb
- module Generators
- class ScaffoldGenerator < Base
+module Erb # :nodoc:
+ module Generators # :nodoc:
+ class ScaffoldGenerator < Base # :nodoc:
include Rails::Generators::ResourceHelpers
- argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_root_folder
empty_directory File.join("app/views", controller_file_path)
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index d2c2abf40c..d8a4f15b4b 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -2,7 +2,7 @@ require 'active_support/time'
module Rails
module Generators
- class GeneratedAttribute
+ class GeneratedAttribute # :nodoc:
INDEX_OPTIONS = %w(index uniq)
UNIQ_INDEX_OPTIONS = %w(uniq)
@@ -23,7 +23,7 @@ module Rails
type = type.to_sym if type
if type && reference?(type)
- references_index = UNIQ_INDEX_OPTIONS.include?(has_index) ? { :unique => true } : true
+ references_index = UNIQ_INDEX_OPTIONS.include?(has_index) ? { unique: true } : true
attr_options[:index] = references_index
end
@@ -41,11 +41,11 @@ module Rails
def parse_type_and_options(type)
case type
when /(string|text|binary|integer)\{(\d+)\}/
- return $1, :limit => $2.to_i
+ return $1, limit: $2.to_i
when /decimal\{(\d+)[,.-](\d+)\}/
- return :decimal, :precision => $1.to_i, :scale => $2.to_i
+ return :decimal, precision: $1.to_i, scale: $2.to_i
when /(references|belongs_to)\{polymorphic\}/
- return $1, :polymorphic => true
+ return $1, polymorphic: true
else
return type, {}
end
diff --git a/railties/lib/rails/generators/js/assets/assets_generator.rb b/railties/lib/rails/generators/js/assets/assets_generator.rb
index d134a9e392..1e925b2cd2 100644
--- a/railties/lib/rails/generators/js/assets/assets_generator.rb
+++ b/railties/lib/rails/generators/js/assets/assets_generator.rb
@@ -1,8 +1,8 @@
require "rails/generators/named_base"
-module Js
- module Generators
- class AssetsGenerator < Rails::Generators::NamedBase
+module Js # :nodoc:
+ module Generators # :nodoc:
+ class AssetsGenerator < Rails::Generators::NamedBase # :nodoc:
source_root File.expand_path("../templates", __FILE__)
def copy_javascript
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index b61a5fc69d..84f8f76838 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -5,9 +5,9 @@ require 'rails/generators/generated_attribute'
module Rails
module Generators
class NamedBase < Base
- argument :name, :type => :string
- class_option :skip_namespace, :type => :boolean, :default => false,
- :desc => "Skip namespace (affects only isolated applications)"
+ argument :name, type: :string
+ class_option :skip_namespace, type: :boolean, default: false,
+ desc: "Skip namespace (affects only isolated applications)"
def initialize(args, *options) #:nodoc:
@inside_template = nil
@@ -169,7 +169,7 @@ module Rails
#
# ==== Examples
#
- # check_class_collision :suffix => "Observer"
+ # check_class_collision suffix: "Observer"
#
# If the generator is invoked with class name Admin, it will check for
# the presence of "AdminObserver".
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index b71b16b043..18637451ac 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -1,7 +1,7 @@
require 'rails/generators/app_base'
module Rails
- module ActionMethods
+ module ActionMethods # :nodoc:
attr_reader :options
def initialize(generator)
@@ -96,7 +96,7 @@ module Rails
end
def public_directory
- directory "public", "public", :recursive => false
+ directory "public", "public", recursive: false
if options[:skip_index_html]
remove_file "public/index.html"
remove_file 'app/assets/images/rails.png'
@@ -108,14 +108,16 @@ module Rails
directory "script" do |content|
"#{shebang}\n" + content
end
- chmod "script", 0755, :verbose => false
+ chmod "script", 0755, verbose: false
end
def test
empty_directory_with_keep_file 'test/fixtures'
- empty_directory_with_keep_file 'test/functional'
+ empty_directory_with_keep_file 'test/controllers'
+ empty_directory_with_keep_file 'test/mailers'
+ empty_directory_with_keep_file 'test/models'
+ empty_directory_with_keep_file 'test/helpers'
empty_directory_with_keep_file 'test/integration'
- empty_directory_with_keep_file 'test/unit'
template 'test/performance/browsing_test.rb'
template 'test/test_helper.rb'
@@ -146,12 +148,12 @@ module Rails
RAILS_DEV_PATH = File.expand_path("../../../../../..", File.dirname(__FILE__))
RESERVED_NAMES = %w[application destroy benchmarker profiler plugin runner test]
- class AppGenerator < AppBase
+ class AppGenerator < AppBase # :nodoc:
add_shared_options_for "application"
# Add bin/rails options
- class_option :version, :type => :boolean, :aliases => "-v", :group => :rails,
- :desc => "Show Rails version number and quit"
+ class_option :version, type: :boolean, aliases: "-v", group: :rails,
+ desc: "Show Rails version number and quit"
def initialize(*args)
raise Error, "Options should be given after the application name. For details run: rails --help" if args[0].blank?
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index 55a6b3f4f2..30f8a5f75e 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -12,7 +12,7 @@ source 'https://rubygems.org'
# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'
-# To use Jbuilder templates for JSON
+# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
# gem 'jbuilder'
# Use unicorn as the app server
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
index f33a7f4ac2..7342bffd9d 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
@@ -5,7 +5,7 @@
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-// the compiled file.
+// compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
@@ -13,5 +13,6 @@
<% unless options[:skip_javascript] -%>
//= require <%= options[:javascript] %>
//= require <%= options[:javascript] %>_ujs
+//= require turbolinks
<% end -%>
//= require_tree .
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 39275e4285..5f15c973c6 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -7,16 +7,12 @@ require 'rails/all'
<%= comment_if :skip_active_record %>require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
-<%= comment_if :skip_sprockets %>require "sprockets/rails/railtie"
+<%= comment_if :skip_sprockets %>require "sprockets/railtie"
<%= comment_if :skip_test_unit %>require "rails/test_unit/railtie"
<% end -%>
-if defined?(Bundler)
- # If you precompile assets before deploying to production, use this line.
- Bundler.require(*Rails.groups(assets: %w(development test)))
- # If you want your assets lazily compiled in production, use this line.
- # Bundler.require(:default, :assets, Rails.env)
-end
+# Assets should be precompiled for production (so we don't need the gems loaded then)
+Bundler.require(*Rails.groups(assets: %w(development test)))
module <%= app_const_base %>
class Application < Rails::Application
@@ -27,17 +23,6 @@ module <%= app_const_base %>
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
- # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
- # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
- # config.time_zone = 'Central Time (US & Canada)'
-
- # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
- # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
- # config.i18n.default_locale = :de
-
- # Configure the default encoding used in templates for Ruby 1.9.
- config.encoding = "utf-8"
-
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
index 07223a71c9..22c9194fad 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml
@@ -2,7 +2,9 @@
#
# Install the pg driver:
# gem install pg
-# On Mac OS X with macports:
+# On OS X with Homebrew:
+# gem install pg -- --with-pg-config=/usr/local/bin/pg_config
+# On OS X with MacPorts:
# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
# On Windows:
# gem install pg
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index 6b5b3a0b1f..bd0a0d44b8 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -32,9 +32,6 @@
<%- end -%>
<%- unless options.skip_sprockets? -%>
- # Do not compress assets.
- config.assets.compress = false
-
# Debug mode disables concatenation and preprocessing of assets.
config.assets.debug = true
<%- end -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index cb3e8b123e..14422e275e 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -14,12 +14,18 @@
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
+ # Enable Rack::Cache to put a simple HTTP cache in front of your application
+ # Add `rack-cache` to your Gemfile before enabling this.
+ # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
+ # config.action_dispatch.rack_cache = true
+
# Disable Rails's static asset server (Apache or nginx will already do this).
config.serve_static_assets = false
<%- unless options.skip_sprockets? -%>
# Compress JavaScripts and CSS.
- config.assets.compress = true
+ config.assets.js_compressor = :uglifier
+ # config.assets.css_compressor = :sass
# Don't fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = false
@@ -55,7 +61,8 @@
# config.assets.precompile += %w( search.js )
<%- end -%>
- # Disable delivery errors, bad email addresses will be ignored.
+ # Ignore bad email addresses and do not raise email delivery errors.
+ # Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
# Enable threaded mode.
@@ -82,5 +89,5 @@
# Default the production mode queue to an synchronous queue. You will probably
# want to replace this with an out-of-process queueing solution.
- # config.queue = ActiveSupport::SynchronousQueue
+ # config.queue = ActiveSupport::SynchronousQueue.new
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
index 202fc98adf..a5ef0cd9cd 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
@@ -34,6 +34,6 @@
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
- # Use the testing queue.
- config.queue = ActiveSupport::TestQueue
+ # Use the synchronous queue to run jobs immediately.
+ config.queue = ActiveSupport::SynchronousQueue.new
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb
new file mode 100644
index 0000000000..a8285f88ca
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb
@@ -0,0 +1,7 @@
+# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
+# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
+# Rails.application.config.time_zone = 'Central Time (US & Canada)'
+
+# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
+# Rails.application.config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
+# Rails.application.config.i18n.default_locale = :de
diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
index f6b1ef1feb..631543c705 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
@@ -1,23 +1,20 @@
<%= app_const %>.routes.draw do
- # The priority is based upon order of creation:
- # first created -> highest priority.
+ # The priority is based upon order of creation: first created -> highest priority.
+ # See how all your routes lay out with "rake routes".
- # You can have the root of your site routed with "root"
- # just remember to delete public/index.html.
+ # You can have the root of your site routed with "root" just remember to delete public/index.html.
# root to: 'welcome#index'
- # Sample of regular route:
+ # Example of regular route:
# get 'products/:id' => 'catalog#view'
- # Keep in mind you can assign values other than :controller and :action.
- # Sample of named route:
+ # Example of named route that can be invoked with purchase_url(id: product.id)
# get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
- # This route can be invoked with purchase_url(id: product.id).
- # Sample resource route (maps HTTP verbs to controller actions automatically):
+ # Example resource route (maps HTTP verbs to controller actions automatically):
# resources :products
- # Sample resource route with options:
+ # Example resource route with options:
# resources :products do
# member do
# get 'short'
@@ -29,13 +26,13 @@
# end
# end
- # Sample resource route with sub-resources:
+ # Example resource route with sub-resources:
# resources :products do
# resources :comments, :sales
# resource :seller
# end
- # Sample resource route with more complex sub-resources:
+ # Example resource route with more complex sub-resources:
# resources :products do
# resources :comments
# resources :sales do
@@ -43,13 +40,10 @@
# end
# end
- # Sample resource route within a namespace:
+ # Example resource route within a namespace:
# namespace :admin do
# # Directs /admin/products/* to Admin::ProductsController
# # (app/controllers/admin/products_controller.rb)
# resources :products
# end
-
-
- # See how all your routes lay out with "rake routes".
end
diff --git a/railties/lib/rails/generators/rails/assets/assets_generator.rb b/railties/lib/rails/generators/rails/assets/assets_generator.rb
index 2e7f25a0b7..6f4b86e708 100644
--- a/railties/lib/rails/generators/rails/assets/assets_generator.rb
+++ b/railties/lib/rails/generators/rails/assets/assets_generator.rb
@@ -1,11 +1,11 @@
module Rails
module Generators
- class AssetsGenerator < NamedBase
- class_option :javascripts, :type => :boolean, :desc => "Generate JavaScripts"
- class_option :stylesheets, :type => :boolean, :desc => "Generate Stylesheets"
+ class AssetsGenerator < NamedBase # :nodoc:
+ class_option :javascripts, type: :boolean, desc: "Generate JavaScripts"
+ class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
- class_option :javascript_engine, :desc => "Engine for JavaScripts"
- class_option :stylesheet_engine, :desc => "Engine for Stylesheets"
+ class_option :javascript_engine, desc: "Engine for JavaScripts"
+ class_option :stylesheet_engine, desc: "Engine for Stylesheets"
protected
diff --git a/railties/lib/rails/generators/rails/controller/USAGE b/railties/lib/rails/generators/rails/controller/USAGE
index b658777b12..9def4af65c 100644
--- a/railties/lib/rails/generators/rails/controller/USAGE
+++ b/railties/lib/rails/generators/rails/controller/USAGE
@@ -12,7 +12,7 @@ Example:
`rails generate controller CreditCards open debit credit close`
CreditCards controller with URLs like /credit_cards/debit.
- Controller: app/controllers/credit_cards_controller.rb
- Functional Test: test/functional/credit_cards_controller_test.rb
- Views: app/views/credit_cards/debit.html.erb [...]
- Helper: app/helpers/credit_cards_helper.rb
+ Controller: app/controllers/credit_cards_controller.rb
+ Test: test/controllers/credit_cards_controller_test.rb
+ Views: app/views/credit_cards/debit.html.erb [...]
+ Helper: app/helpers/credit_cards_helper.rb
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
index 74aa0432a8..bae54623c6 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -1,8 +1,8 @@
module Rails
module Generators
- class ControllerGenerator < NamedBase
- argument :actions, :type => :array, :default => [], :banner => "action action"
- check_class_collision :suffix => "Controller"
+ class ControllerGenerator < NamedBase # :nodoc:
+ argument :actions, type: :array, default: [], banner: "action action"
+ check_class_collision suffix: "Controller"
def create_controller_files
template 'controller.rb', File.join('app/controllers', class_path, "#{file_name}_controller.rb")
diff --git a/railties/lib/rails/generators/rails/generator/generator_generator.rb b/railties/lib/rails/generators/rails/generator/generator_generator.rb
index 3e0a442bda..9a7a516b5b 100644
--- a/railties/lib/rails/generators/rails/generator/generator_generator.rb
+++ b/railties/lib/rails/generators/rails/generator/generator_generator.rb
@@ -1,10 +1,10 @@
module Rails
module Generators
- class GeneratorGenerator < NamedBase
- check_class_collision :suffix => "Generator"
+ class GeneratorGenerator < NamedBase # :nodoc:
+ check_class_collision suffix: "Generator"
- class_option :namespace, :type => :boolean, :default => true,
- :desc => "Namespace generator under lib/generators/name"
+ class_option :namespace, type: :boolean, default: true,
+ desc: "Namespace generator under lib/generators/name"
def create_generator_files
directory '.', generator_dir
diff --git a/railties/lib/rails/generators/rails/helper/USAGE b/railties/lib/rails/generators/rails/helper/USAGE
index c0ddb0f606..30e323a858 100644
--- a/railties/lib/rails/generators/rails/helper/USAGE
+++ b/railties/lib/rails/generators/rails/helper/USAGE
@@ -13,5 +13,5 @@ Example:
Credit card helper.
Helper: app/helpers/credit_card_helper.rb
- Test: test/unit/helpers/credit_card_helper_test.rb
+ Test: test/helpers/credit_card_helper_test.rb
diff --git a/railties/lib/rails/generators/rails/helper/helper_generator.rb b/railties/lib/rails/generators/rails/helper/helper_generator.rb
index ad66388591..5ff38e4111 100644
--- a/railties/lib/rails/generators/rails/helper/helper_generator.rb
+++ b/railties/lib/rails/generators/rails/helper/helper_generator.rb
@@ -1,7 +1,7 @@
module Rails
module Generators
- class HelperGenerator < NamedBase
- check_class_collision :suffix => "Helper"
+ class HelperGenerator < NamedBase # :nodoc:
+ check_class_collision suffix: "Helper"
def create_helper_files
template 'helper.rb', File.join('app/helpers', class_path, "#{file_name}_helper.rb")
diff --git a/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb b/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb
index 363a327fcb..70770ddcb8 100644
--- a/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb
+++ b/railties/lib/rails/generators/rails/integration_test/integration_test_generator.rb
@@ -1,7 +1,7 @@
module Rails
module Generators
- class IntegrationTestGenerator < NamedBase
- hook_for :integration_tool, :as => :integration
+ class IntegrationTestGenerator < NamedBase # :nodoc:
+ hook_for :integration_tool, as: :integration
end
end
end
diff --git a/railties/lib/rails/generators/rails/migration/migration_generator.rb b/railties/lib/rails/generators/rails/migration/migration_generator.rb
index f87dce1502..965c42db36 100644
--- a/railties/lib/rails/generators/rails/migration/migration_generator.rb
+++ b/railties/lib/rails/generators/rails/migration/migration_generator.rb
@@ -1,8 +1,8 @@
module Rails
module Generators
- class MigrationGenerator < NamedBase #metagenerator
- argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
- hook_for :orm, :required => true
+ class MigrationGenerator < NamedBase # :nodoc:
+ argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
+ hook_for :orm, required: true
end
end
end
diff --git a/railties/lib/rails/generators/rails/model/USAGE b/railties/lib/rails/generators/rails/model/USAGE
index c46c86076e..e29e19490e 100644
--- a/railties/lib/rails/generators/rails/model/USAGE
+++ b/railties/lib/rails/generators/rails/model/USAGE
@@ -74,7 +74,7 @@ Examples:
For ActiveRecord and TestUnit it creates:
Model: app/models/account.rb
- Test: test/unit/account_test.rb
+ Test: test/models/account_test.rb
Fixtures: test/fixtures/accounts.yml
Migration: db/migrate/XXX_add_accounts.rb
@@ -88,7 +88,7 @@ Examples:
Module: app/models/admin.rb
Model: app/models/admin/account.rb
- Test: test/unit/admin/account_test.rb
+ Test: test/models/admin/account_test.rb
Fixtures: test/fixtures/admin/accounts.yml
Migration: db/migrate/XXX_add_admin_accounts.rb
diff --git a/railties/lib/rails/generators/rails/model/model_generator.rb b/railties/lib/rails/generators/rails/model/model_generator.rb
index 9bb29b784e..ea3d69d7c9 100644
--- a/railties/lib/rails/generators/rails/model/model_generator.rb
+++ b/railties/lib/rails/generators/rails/model/model_generator.rb
@@ -1,8 +1,8 @@
module Rails
module Generators
- class ModelGenerator < NamedBase #metagenerator
- argument :attributes, :type => :array, :default => [], :banner => "field[:type][:index] field[:type][:index]"
- hook_for :orm, :required => true
+ class ModelGenerator < NamedBase # :nodoc:
+ argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
+ hook_for :orm, required: true
end
end
end
diff --git a/railties/lib/rails/generators/rails/observer/USAGE b/railties/lib/rails/generators/rails/observer/USAGE
index d8f32a6a48..177ff49e4a 100644
--- a/railties/lib/rails/generators/rails/observer/USAGE
+++ b/railties/lib/rails/generators/rails/observer/USAGE
@@ -9,4 +9,4 @@ Example:
For ActiveRecord and TestUnit it creates:
Observer: app/models/account_observer.rb
- TestUnit: test/unit/account_observer_test.rb
+ TestUnit: test/models/account_observer_test.rb
diff --git a/railties/lib/rails/generators/rails/observer/observer_generator.rb b/railties/lib/rails/generators/rails/observer/observer_generator.rb
index f5cedee91f..7a4d701ac6 100644
--- a/railties/lib/rails/generators/rails/observer/observer_generator.rb
+++ b/railties/lib/rails/generators/rails/observer/observer_generator.rb
@@ -1,7 +1,7 @@
module Rails
module Generators
- class ObserverGenerator < NamedBase #metagenerator
- hook_for :orm, :required => true
+ class ObserverGenerator < NamedBase # :nodoc:
+ hook_for :orm, required: true
end
end
end
diff --git a/railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb b/railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb
index d1c71ab8ed..56cd562f3d 100644
--- a/railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb
+++ b/railties/lib/rails/generators/rails/performance_test/performance_test_generator.rb
@@ -1,7 +1,7 @@
module Rails
module Generators
- class PerformanceTestGenerator < NamedBase
- hook_for :performance_tool, :as => :performance
+ class PerformanceTestGenerator < NamedBase # :nodoc:
+ hook_for :performance_tool, as: :performance
end
end
end
diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
index c77b3450a3..4a0bcc35a4 100644
--- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
@@ -3,6 +3,13 @@ require "rails/generators/rails/app/app_generator"
require 'date'
module Rails
+ # The plugin builder allows you to override elements of the plugin
+ # generator without being forced to reverse the operations of the default
+ # generator.
+ #
+ # This allows you to override entire operations, like the creation of the
+ # Gemfile, README, or JavaScript files, without needing to know exactly
+ # what those operations do so you can create another template action.
class PluginBuilder
def rakefile
template "Rakefile"
@@ -61,7 +68,7 @@ module Rails
append_file "Rakefile", <<-EOF
#{rakefile_test_tasks}
-task :default => :test
+task default: :test
EOF
if full?
template "test/integration/navigation_test.rb"
@@ -82,10 +89,10 @@ task :default => :test
end
def test_dummy_config
- template "rails/boot.rb", "#{dummy_path}/config/boot.rb", :force => true
- template "rails/application.rb", "#{dummy_path}/config/application.rb", :force => true
+ template "rails/boot.rb", "#{dummy_path}/config/boot.rb", force: true
+ template "rails/application.rb", "#{dummy_path}/config/application.rb", force: true
if mountable?
- template "rails/routes.rb", "#{dummy_path}/config/routes.rb", :force => true
+ template "rails/routes.rb", "#{dummy_path}/config/routes.rb", force: true
end
end
@@ -128,10 +135,10 @@ task :default => :test
def script(force = false)
return unless full?
- directory "script", :force => force do |content|
+ directory "script", force: force do |content|
"#{shebang}\n" + content
end
- chmod "script", 0755, :verbose => false
+ chmod "script", 0755, verbose: false
end
def gemfile_entry
@@ -146,25 +153,25 @@ task :default => :test
end
module Generators
- class PluginNewGenerator < AppBase
+ class PluginNewGenerator < AppBase # :nodoc:
add_shared_options_for "plugin"
alias_method :plugin_path, :app_path
- class_option :dummy_path, :type => :string, :default => "test/dummy",
- :desc => "Create dummy application at given path"
+ class_option :dummy_path, type: :string, default: "test/dummy",
+ desc: "Create dummy application at given path"
- class_option :full, :type => :boolean, :default => false,
- :desc => "Generate a rails engine with bundled Rails application for testing"
+ class_option :full, type: :boolean, default: false,
+ desc: "Generate a rails engine with bundled Rails application for testing"
- class_option :mountable, :type => :boolean, :default => false,
- :desc => "Generate mountable isolated application"
+ class_option :mountable, type: :boolean, default: false,
+ desc: "Generate mountable isolated application"
- class_option :skip_gemspec, :type => :boolean, :default => false,
- :desc => "Skip gemspec file"
+ class_option :skip_gemspec, type: :boolean, default: false,
+ desc: "Skip gemspec file"
- class_option :skip_gemfile_entry, :type => :boolean, :default => false,
- :desc => "If creating plugin in application's directory " +
+ class_option :skip_gemfile_entry, type: :boolean, default: false,
+ desc: "If creating plugin in application's directory " +
"skip adding entry to Gemfile"
def initialize(*args)
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt b/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt
index bd983fb90f..1d380420b4 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt
@@ -2,7 +2,7 @@
<html>
<head>
<title><%= camelized %></title>
- <%%= stylesheet_link_tag "<%= name %>/application", :media => "all" %>
+ <%%= stylesheet_link_tag "<%= name %>/application", media: "all" %>
<%%= javascript_include_tag "<%= name %>/application" %>
<%%= csrf_meta_tags %>
</head>
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb b/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb
index 2f9b7fc962..8a8ba04a70 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/rails/application.rb
@@ -7,7 +7,7 @@ require 'rails/all'
<%= comment_if :skip_active_record %>require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
-<%= comment_if :skip_sprockets %>require "sprockets/rails/railtie"
+<%= comment_if :skip_sprockets %>require "sprockets/railtie"
<%= comment_if :skip_test_unit %>require "rails/test_unit/railtie"
<% end -%>
diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb
index 3a0586ee43..8014feb75f 100644
--- a/railties/lib/rails/generators/rails/resource/resource_generator.rb
+++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb
@@ -4,17 +4,17 @@ require 'active_support/core_ext/object/blank'
module Rails
module Generators
- class ResourceGenerator < ModelGenerator #metagenerator
+ class ResourceGenerator < ModelGenerator # :nodoc:
include ResourceHelpers
- hook_for :resource_controller, :required => true do |controller|
+ hook_for :resource_controller, required: true do |controller|
invoke controller, [ controller_name, options[:actions] ]
end
- class_option :actions, :type => :array, :banner => "ACTION ACTION", :default => [],
- :desc => "Actions for the resource controller"
+ class_option :actions, type: :array, banner: "ACTION ACTION", default: [],
+ desc: "Actions for the resource controller"
- hook_for :resource_route, :required => true
+ hook_for :resource_route, required: true
end
end
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 6a5d62803c..121205b254 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,13 +1,50 @@
module Rails
module Generators
- class ResourceRouteGenerator < NamedBase
+ class ResourceRouteGenerator < NamedBase # :nodoc:
+
+ # Properly nests namespaces passed into a generator
+ #
+ # $ rails generate resource admin/users/products
+ #
+ # should give you
+ #
+ # namespace :admin do
+ # namespace :users
+ # resources :products
+ # end
+ # end
def add_resource_route
return if options[:actions].present?
- route_config = regular_class_path.collect{ |namespace| "namespace :#{namespace} do " }.join(" ")
- route_config << "resources :#{file_name.pluralize}"
- route_config << " end" * regular_class_path.size
- route route_config
+
+ # iterates over all namespaces and opens up blocks
+ regular_class_path.each_with_index do |namespace, index|
+ write("namespace :#{namespace} do", index + 1)
+ end
+
+ # inserts the primary resource
+ write("resources :#{file_name.pluralize}", route_length + 1)
+
+ # ends blocks
+ regular_class_path.each_index do |index|
+ write("end", route_length - index)
+ end
+
+ # route prepends two spaces onto the front of the string that is passed, this corrects that
+ route route_string[2..-1]
end
+
+ private
+ def route_string
+ @route_string ||= ""
+ end
+
+ def write(str, indent)
+ route_string << "#{" " * indent}#{str}\n"
+ end
+
+ def route_length
+ regular_class_path.length
+ end
end
end
end
diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
index 03a61a035e..b4f466fbd8 100644
--- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
@@ -2,14 +2,14 @@ require 'rails/generators/rails/resource/resource_generator'
module Rails
module Generators
- class ScaffoldGenerator < ResourceGenerator #metagenerator
+ class ScaffoldGenerator < ResourceGenerator # :nodoc:
remove_hook_for :resource_controller
remove_class_option :actions
- class_option :stylesheets, :type => :boolean, :desc => "Generate Stylesheets"
- class_option :stylesheet_engine, :desc => "Engine for Stylesheets"
+ class_option :stylesheets, type: :boolean, desc: "Generate Stylesheets"
+ class_option :stylesheet_engine, desc: "Engine for Stylesheets"
- hook_for :scaffold_controller, :required => true
+ hook_for :scaffold_controller, required: true
hook_for :assets do |assets|
invoke assets, [controller_name]
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/USAGE b/railties/lib/rails/generators/rails/scaffold_controller/USAGE
index 5cd51b62d4..8ba4c5ccbc 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/USAGE
+++ b/railties/lib/rails/generators/rails/scaffold_controller/USAGE
@@ -13,7 +13,7 @@ Example:
`rails generate scaffold_controller CreditCard`
Credit card controller with URLs like /credit_card/debit.
- Controller: app/controllers/credit_cards_controller.rb
- Functional Test: test/functional/credit_cards_controller_test.rb
- Views: app/views/credit_cards/index.html.erb [...]
- Helper: app/helpers/credit_cards_helper.rb
+ Controller: app/controllers/credit_cards_controller.rb
+ Test: test/controllers/credit_cards_controller_test.rb
+ Views: app/views/credit_cards/index.html.erb [...]
+ Helper: app/helpers/credit_cards_helper.rb
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 f30ad6e20d..4f36b612ae 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
+++ b/railties/lib/rails/generators/rails/scaffold_controller/scaffold_controller_generator.rb
@@ -2,24 +2,24 @@ require 'rails/generators/resource_helpers'
module Rails
module Generators
- class ScaffoldControllerGenerator < NamedBase
+ class ScaffoldControllerGenerator < NamedBase # :nodoc:
include ResourceHelpers
- check_class_collision :suffix => "Controller"
+ check_class_collision suffix: "Controller"
- class_option :orm, :banner => "NAME", :type => :string, :required => true,
- :desc => "ORM to generate the controller for"
+ class_option :orm, banner: "NAME", type: :string, required: true,
+ desc: "ORM to generate the controller for"
- argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_controller_files
template "controller.rb", File.join('app/controllers', class_path, "#{controller_file_name}_controller.rb")
end
- hook_for :template_engine, :test_framework, :as => :scaffold
+ hook_for :template_engine, :test_framework, as: :scaffold
# Invoke the helper using the controller name (pluralized)
- hook_for :helper, :as => :scaffold do |invoked|
+ hook_for :helper, as: :scaffold do |invoked|
invoke invoked, [ controller_name ]
end
end
diff --git a/railties/lib/rails/generators/rails/task/task_generator.rb b/railties/lib/rails/generators/rails/task/task_generator.rb
index 8a62d9e8eb..754824ca0c 100644
--- a/railties/lib/rails/generators/rails/task/task_generator.rb
+++ b/railties/lib/rails/generators/rails/task/task_generator.rb
@@ -1,7 +1,7 @@
module Rails
module Generators
- class TaskGenerator < NamedBase
- argument :actions, :type => :array, :default => [], :banner => "action action"
+ class TaskGenerator < NamedBase # :nodoc:
+ argument :actions, type: :array, default: [], banner: "action action"
def create_task_files
template 'task.rb', File.join('lib/tasks', "#{file_name}.rake")
diff --git a/railties/lib/rails/generators/rails/task/templates/task.rb b/railties/lib/rails/generators/rails/task/templates/task.rb
index b7407bd6dc..1e3ed5f158 100644
--- a/railties/lib/rails/generators/rails/task/templates/task.rb
+++ b/railties/lib/rails/generators/rails/task/templates/task.rb
@@ -1,7 +1,7 @@
namespace :<%= file_name %> do
<% actions.each do |action| -%>
desc "TODO"
- task :<%= action %> => :environment do
+ task <%= action %>: :environment do
end
<% end -%>
diff --git a/railties/lib/rails/generators/resource_helpers.rb b/railties/lib/rails/generators/resource_helpers.rb
index 48833869e5..7fd5c00768 100644
--- a/railties/lib/rails/generators/resource_helpers.rb
+++ b/railties/lib/rails/generators/resource_helpers.rb
@@ -4,16 +4,14 @@ module Rails
module Generators
# Deal with controller names on scaffold and add some helpers to deal with
# ActiveModel.
- #
- module ResourceHelpers
+ module ResourceHelpers # :nodoc:
mattr_accessor :skip_warn
def self.included(base) #:nodoc:
- base.class_option :force_plural, :type => :boolean, :desc => "Forces the use of a plural ModelName"
+ base.class_option :force_plural, type: :boolean, desc: "Forces the use of a plural ModelName"
end
# Set controller variables on initialization.
- #
def initialize(*args) #:nodoc:
super
diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb
index 2ff340755a..24308dcf6c 100644
--- a/railties/lib/rails/generators/test_case.rb
+++ b/railties/lib/rails/generators/test_case.rb
@@ -26,7 +26,6 @@ module Rails
# destination File.expand_path("../tmp", File.dirname(__FILE__))
# setup :prepare_destination
# end
- #
class TestCase < ActiveSupport::TestCase
include FileUtils
@@ -37,13 +36,13 @@ module Rails
self.current_path = File.expand_path(Dir.pwd)
self.default_arguments = []
- def setup
+ def setup # :nodoc:
destination_root_is_set?
ensure_current_path
super
end
- def teardown
+ def teardown # :nodoc:
ensure_current_path
super
end
@@ -51,7 +50,6 @@ module Rails
# Sets which generator should be tested:
#
# tests AppGenerator
- #
def self.tests(klass)
self.generator_class = klass
end
@@ -60,7 +58,6 @@ module Rails
# invoking it.
#
# arguments %w(app_name --skip-active-record)
- #
def self.arguments(array)
self.default_arguments = array
end
@@ -68,7 +65,6 @@ module Rails
# Sets the destination of generator files:
#
# destination File.expand_path("../tmp", File.dirname(__FILE__))
- #
def self.destination(path)
self.destination_root = path
end
@@ -91,7 +87,6 @@ module Rails
# assert_match(/Product\.all/, index)
# end
# end
- #
def assert_file(relative, *contents)
absolute = File.expand_path(relative, destination_root)
assert File.exists?(absolute), "Expected file #{relative.inspect} to exist, but does not"
@@ -114,7 +109,6 @@ module Rails
# path relative to the configured destination:
#
# assert_no_file "config/random.rb"
- #
def assert_no_file(relative)
absolute = File.expand_path(relative, destination_root)
assert !File.exists?(absolute), "Expected file #{relative.inspect} to not exist, but does"
@@ -132,7 +126,6 @@ module Rails
# assert_file "db/migrate/003_create_products.rb"
#
# Consequently, assert_migration accepts the same arguments has assert_file.
- #
def assert_migration(relative, *contents, &block)
file_name = migration_file_name(relative)
assert file_name, "Expected migration #{relative} to exist, but was not found"
@@ -143,7 +136,6 @@ module Rails
# path relative to the configured destination:
#
# assert_no_migration "db/migrate/create_products.rb"
- #
def assert_no_migration(relative)
file_name = migration_file_name(relative)
assert_nil file_name, "Expected migration #{relative} to not exist, but found #{file_name}"
@@ -158,7 +150,6 @@ module Rails
# assert_match(/create_table/, up)
# end
# end
- #
def assert_class_method(method, content, &block)
assert_instance_method "self.#{method}", content, &block
end
@@ -171,7 +162,6 @@ module Rails
# assert_match(/Product\.all/, index)
# end
# end
- #
def assert_instance_method(method, content)
assert content =~ /def #{method}(\(.+\))?(.*?)\n end/m, "Expected to have method #{method}"
yield $2.strip if block_given?
@@ -182,7 +172,6 @@ module Rails
# properly:
#
# assert_field_type :date, :date_select
- #
def assert_field_type(attribute_type, field_type)
assert_equal(field_type, create_generated_attribute(attribute_type).field_type)
end
@@ -190,7 +179,6 @@ module Rails
# Asserts the given attribute type gets a proper default value:
#
# assert_field_default_value :string, "MyString"
- #
def assert_field_default_value(attribute_type, value)
assert_equal(value, create_generated_attribute(attribute_type).default)
end
@@ -212,39 +200,38 @@ module Rails
# You can provide a configuration hash as second argument. This method returns the output
# printed by the generator.
def run_generator(args=self.default_arguments, config={})
- capture(:stdout) { self.generator_class.start(args, config.reverse_merge(:destination_root => destination_root)) }
+ capture(:stdout) { self.generator_class.start(args, config.reverse_merge(destination_root: destination_root)) }
end
# Instantiate the generator.
def generator(args=self.default_arguments, options={}, config={})
- @generator ||= self.generator_class.new(args, options, config.reverse_merge(:destination_root => destination_root))
+ @generator ||= self.generator_class.new(args, options, config.reverse_merge(destination_root: destination_root))
end
# Create a Rails::Generators::GeneratedAttribute by supplying the
# attribute type and, optionally, the attribute name:
#
# create_generated_attribute(:string, 'name')
- #
def create_generated_attribute(attribute_type, name = 'test', index = nil)
Rails::Generators::GeneratedAttribute.parse([name, attribute_type, index].compact.join(':'))
end
protected
- def destination_root_is_set? #:nodoc:
+ def destination_root_is_set? # :nodoc:
raise "You need to configure your Rails::Generators::TestCase destination root." unless destination_root
end
- def ensure_current_path #:nodoc:
+ def ensure_current_path # :nodoc:
cd current_path
end
- def prepare_destination
+ def prepare_destination # :nodoc:
rm_rf(destination_root)
mkdir_p(destination_root)
end
- def migration_file_name(relative) #:nodoc:
+ def migration_file_name(relative) # :nodoc:
absolute = File.expand_path(relative, destination_root)
dirname, file_name = File.dirname(absolute), File.basename(absolute).sub(/\.rb$/, '')
Dir.glob("#{dirname}/[0-9]*_*.rb").grep(/\d+_#{file_name}.rb$/).first
diff --git a/railties/lib/rails/generators/test_unit.rb b/railties/lib/rails/generators/test_unit.rb
index 62b9afaa2c..fe45c9e15d 100644
--- a/railties/lib/rails/generators/test_unit.rb
+++ b/railties/lib/rails/generators/test_unit.rb
@@ -1,8 +1,8 @@
require 'rails/generators/named_base'
-module TestUnit
- module Generators
- class Base < Rails::Generators::NamedBase #:nodoc:
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class Base < Rails::Generators::NamedBase # :nodoc:
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb
index 20f3bd8965..b5aa581769 100644
--- a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb
@@ -1,14 +1,14 @@
require 'rails/generators/test_unit'
-module TestUnit
- module Generators
- class ControllerGenerator < Base
- argument :actions, :type => :array, :default => [], :banner => "action action"
- check_class_collision :suffix => "ControllerTest"
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class ControllerGenerator < Base # :nodoc:
+ argument :actions, type: :array, default: [], banner: "action action"
+ check_class_collision suffix: "ControllerTest"
def create_test_files
template 'functional_test.rb',
- File.join('test/functional', class_path, "#{file_name}_controller_test.rb")
+ File.join('test/controllers', class_path, "#{file_name}_controller_test.rb")
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb
index 4ea80bf7be..0db76f9eaf 100644
--- a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb
+++ b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb
@@ -1,12 +1,12 @@
require 'rails/generators/test_unit'
-module TestUnit
- module Generators
- class HelperGenerator < Base
- check_class_collision :suffix => "HelperTest"
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class HelperGenerator < Base # :nodoc:
+ check_class_collision suffix: "HelperTest"
def create_helper_files
- template 'helper_test.rb', File.join('test/unit/helpers', class_path, "#{file_name}_helper_test.rb")
+ template 'helper_test.rb', File.join('test/helpers', class_path, "#{file_name}_helper_test.rb")
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
index 32d0fac029..e004835bd5 100644
--- a/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
+++ b/railties/lib/rails/generators/test_unit/integration/integration_generator.rb
@@ -1,9 +1,9 @@
require 'rails/generators/test_unit'
-module TestUnit
- module Generators
- class IntegrationGenerator < Base
- check_class_collision :suffix => "Test"
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class IntegrationGenerator < Base # :nodoc:
+ check_class_collision suffix: "Test"
def create_test_files
template 'integration_test.rb', File.join('test/integration', class_path, "#{file_name}_test.rb")
diff --git a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
index 1a49286d41..3334b189bf 100644
--- a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
+++ b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
@@ -1,13 +1,13 @@
require 'rails/generators/test_unit'
-module TestUnit
- module Generators
- class MailerGenerator < Base
- argument :actions, :type => :array, :default => [], :banner => "method method"
- check_class_collision :suffix => "Test"
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class MailerGenerator < Base # :nodoc:
+ argument :actions, type: :array, default: [], banner: "method method"
+ check_class_collision suffix: "Test"
def create_test_files
- template "functional_test.rb", File.join('test/functional', class_path, "#{file_name}_test.rb")
+ template "functional_test.rb", File.join('test/mailers', class_path, "#{file_name}_test.rb")
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/model/model_generator.rb b/railties/lib/rails/generators/test_unit/model/model_generator.rb
index c1dd535dd3..2801749ffe 100644
--- a/railties/lib/rails/generators/test_unit/model/model_generator.rb
+++ b/railties/lib/rails/generators/test_unit/model/model_generator.rb
@@ -1,15 +1,15 @@
require 'rails/generators/test_unit'
-module TestUnit
- module Generators
- class ModelGenerator < Base
- argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
- class_option :fixture, :type => :boolean
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class ModelGenerator < Base # :nodoc:
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
+ class_option :fixture, type: :boolean
- check_class_collision :suffix => "Test"
+ check_class_collision suffix: "Test"
def create_test_file
- template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")
+ template 'unit_test.rb', File.join('test/models', class_path, "#{file_name}_test.rb")
end
hook_for :fixture_replacement
diff --git a/railties/lib/rails/generators/test_unit/observer/observer_generator.rb b/railties/lib/rails/generators/test_unit/observer/observer_generator.rb
index 6cc1158c21..64fe694a8b 100644
--- a/railties/lib/rails/generators/test_unit/observer/observer_generator.rb
+++ b/railties/lib/rails/generators/test_unit/observer/observer_generator.rb
@@ -1,12 +1,12 @@
require 'rails/generators/test_unit'
-module TestUnit
- module Generators
- class ObserverGenerator < Base
- check_class_collision :suffix => "ObserverTest"
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class ObserverGenerator < Base # :nodoc:
+ check_class_collision suffix: "ObserverTest"
def create_test_files
- template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_observer_test.rb")
+ template 'unit_test.rb', File.join('test/models', class_path, "#{file_name}_observer_test.rb")
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/performance/performance_generator.rb b/railties/lib/rails/generators/test_unit/performance/performance_generator.rb
index 99edda5461..5552edeee4 100644
--- a/railties/lib/rails/generators/test_unit/performance/performance_generator.rb
+++ b/railties/lib/rails/generators/test_unit/performance/performance_generator.rb
@@ -1,9 +1,9 @@
require 'rails/generators/test_unit'
-module TestUnit
- module Generators
- class PerformanceGenerator < Base
- check_class_collision :suffix => "Test"
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class PerformanceGenerator < Base # :nodoc:
+ check_class_collision suffix: "Test"
def create_test_files
template 'performance_test.rb', File.join('test/performance', class_path, "#{file_name}_test.rb")
diff --git a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb
index 2f25dcf832..2bcb482d68 100644
--- a/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb
+++ b/railties/lib/rails/generators/test_unit/performance/templates/performance_test.rb
@@ -3,8 +3,8 @@ require 'rails/performance_test_help'
class <%= class_name %>Test < ActionDispatch::PerformanceTest
# Refer to the documentation for all available options
- # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory],
- # :output => 'tmp/performance', :formats => [:flat] }
+ # self.profile_options = { runs: 5, metrics: [:wall_time, :memory],
+ # output: 'tmp/performance', formats: [:flat] }
test "homepage" do
get '/'
diff --git a/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb
index 4d65cd7d89..b5d4f38444 100644
--- a/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/test_unit/plugin/plugin_generator.rb
@@ -1,9 +1,9 @@
require 'rails/generators/test_unit'
-module TestUnit
- module Generators
- class PluginGenerator < Base
- check_class_collision :suffix => "Test"
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class PluginGenerator < Base # :nodoc:
+ check_class_collision suffix: "Test"
def create_test_files
directory '.', 'test'
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 ca7fee3b6e..3b4fec2e83 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
@@ -1,18 +1,18 @@
require 'rails/generators/test_unit'
require 'rails/generators/resource_helpers'
-module TestUnit
- module Generators
- class ScaffoldGenerator < Base
+module TestUnit # :nodoc:
+ module Generators # :nodoc:
+ class ScaffoldGenerator < Base # :nodoc:
include Rails::Generators::ResourceHelpers
- check_class_collision :suffix => "ControllerTest"
+ check_class_collision suffix: "ControllerTest"
- argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_test_files
template "functional_test.rb",
- File.join("test/functional", controller_class_path, "#{controller_file_name}_controller_test.rb")
+ File.join("test/controllers", controller_class_path, "#{controller_file_name}_controller_test.rb")
end
private
diff --git a/railties/lib/rails/info_controller.rb b/railties/lib/rails/info_controller.rb
index 512803aeac..e94c6a2030 100644
--- a/railties/lib/rails/info_controller.rb
+++ b/railties/lib/rails/info_controller.rb
@@ -23,7 +23,7 @@ class Rails::InfoController < ActionController::Base
def require_local!
unless local_request?
- render :text => '<p>For security purposes, this information is only available to local requests.</p>', :status => :forbidden
+ render text: '<p>For security purposes, this information is only available to local requests.</p>', status: :forbidden
end
end
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 3c2210aaf9..8af4130e87 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -5,7 +5,7 @@ module Rails
# paths by a Hash like API. It requires you to give a physical path on initialization.
#
# root = Root.new "/rails"
- # root.add "app/controllers", :eager_load => true
+ # root.add "app/controllers", eager_load: true
#
# The command above creates a new root object and add "app/controllers" as a path.
# This means we can get a <tt>Rails::Paths::Path</tt> object back like below:
@@ -26,7 +26,7 @@ module Rails
# contains the path with the same path value given to +add+. In some situations,
# you may not want this behavior, so you can give :with as option.
#
- # root.add "config/routes", :with => "config/routes.rb"
+ # root.add "config/routes", with: "config/routes.rb"
# root["config/routes"].inspect # => ["config/routes.rb"]
#
# The +add+ method accepts the following options as arguments:
@@ -52,7 +52,7 @@ module Rails
def []=(path, value)
glob = self[path] ? self[path].glob : nil
- add(path, :with => value, :glob => glob)
+ add(path, with: value, glob: glob)
end
def add(path, options={})
@@ -115,7 +115,6 @@ module Rails
class Path
include Enumerable
- attr_reader :path
attr_accessor :glob
def initialize(root, current, paths, options = {})
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
index d0d053e7ed..3f59bb8733 100644
--- a/railties/lib/rails/rack/logger.rb
+++ b/railties/lib/rails/rack/logger.rb
@@ -12,9 +12,6 @@ module Rails
def call(env)
request = ActionDispatch::Request.new(env)
- # Put some space between requests in development logs.
- Rails.logger.info "\n\n" if Rails.env.development?
-
if Rails.logger.respond_to?(:tagged)
Rails.logger.tagged(compute_tags(request)) { call_app(request, env) }
else
@@ -25,6 +22,12 @@ module Rails
protected
def call_app(request, env)
+ # Put some space between requests in development logs.
+ if Rails.env.development?
+ Rails.logger.info ''
+ Rails.logger.info ''
+ end
+
Rails.logger.info started_request_message(request)
@app.call(env)
ensure
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index fba685c769..5b454e7f20 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -172,7 +172,7 @@ module Rails
end
end
- delegate :railtie_name, :to => "self.class"
+ delegate :railtie_name, to: "self.class"
def config
@config ||= Railtie::Configuration.new
diff --git a/railties/lib/rails/railtie/configurable.rb b/railties/lib/rails/railtie/configurable.rb
index 53796c74cf..1572af0b2a 100644
--- a/railties/lib/rails/railtie/configurable.rb
+++ b/railties/lib/rails/railtie/configurable.rb
@@ -6,7 +6,7 @@ module Rails
extend ActiveSupport::Concern
module ClassMethods
- delegate :config, :to => :instance
+ delegate :config, to: :instance
def inherited(base)
raise "You cannot inherit from a #{self.superclass.name} child"
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
index 9dc8843887..0cbbf04da2 100644
--- a/railties/lib/rails/railtie/configuration.rb
+++ b/railties/lib/rails/railtie/configuration.rb
@@ -50,23 +50,23 @@ module Rails
# First configurable block to run. Called before any initializers are run.
def before_configuration(&block)
- ActiveSupport.on_load(:before_configuration, :yield => true, &block)
+ ActiveSupport.on_load(:before_configuration, yield: true, &block)
end
# Third configurable block to run. Does not run if +config.cache_classes+
# set to false.
def before_eager_load(&block)
- ActiveSupport.on_load(:before_eager_load, :yield => true, &block)
+ ActiveSupport.on_load(:before_eager_load, yield: true, &block)
end
# Second configurable block to run. Called before frameworks initialize.
def before_initialize(&block)
- ActiveSupport.on_load(:before_initialize, :yield => true, &block)
+ ActiveSupport.on_load(:before_initialize, yield: true, &block)
end
# Last configurable block to run. Called after frameworks initialize.
def after_initialize(&block)
- ActiveSupport.on_load(:after_initialize, :yield => true, &block)
+ ActiveSupport.on_load(:after_initialize, yield: true, &block)
end
# Array of callbacks defined by #to_prepare.
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index 31e34023c0..3474b02af4 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -32,9 +32,9 @@ class SourceAnnotationExtractor
end
# Prints all annotations with tag +tag+ under the root directories +app+, +config+, +lib+,
- # +script+, and +test+ (recursively). Only filenames with extension
- # +.builder+, +.rb+, +.erb+, +.haml+, +.slim+, +.css+, +.scss+, +.js+, and
- # +.coffee+ are taken into account. The +options+ hash is passed to each
+ # +script+, and +test+ (recursively). Filenames with extension
+ # +.builder+, +.rb+, +.erb+, +.haml+, +.slim+, +.css+, +.scss+, +.js+,
+ # +.coffee+, and +.rake+ are taken into account. The +options+ hash is passed to each
# annotation's +to_s+.
#
# This class method is the single entry point for the rake tasks.
@@ -56,9 +56,9 @@ class SourceAnnotationExtractor
end
# Returns a hash that maps filenames under +dir+ (recursively) to arrays
- # with their annotations. Only files with annotations are included, and only
- # those with extension +.builder+, +.rb+, +.erb+, +.haml+, +.slim+, +.css+,
- # +.scss+, +.js+, and +.coffee+
+ # with their annotations. Only files with annotations are included. Files
+ # with extension +.builder+, +.rb+, +.erb+, +.haml+, +.slim+, +.css+,
+ # +.scss+, +.js+, +.coffee+, and +.rake+
# are taken into account.
def find_in(dir)
results = {}
@@ -68,7 +68,7 @@ class SourceAnnotationExtractor
if File.directory?(item)
results.update(find_in(item))
- elsif item =~ /\.(builder|rb|coffee)$/
+ elsif item =~ /\.(builder|rb|coffee|rake)$/
results.update(extract_annotations_from(item, /#\s*(#{tag}):?\s*(.*)$/))
elsif item =~ /\.(css|scss|js)$/
results.update(extract_annotations_from(item, /\/\/\s*(#{tag}):?\s*(.*)$/))
diff --git a/railties/lib/rails/tasks/annotations.rake b/railties/lib/rails/tasks/annotations.rake
index 9067b03d00..386ecf44be 100644
--- a/railties/lib/rails/tasks/annotations.rake
+++ b/railties/lib/rails/tasks/annotations.rake
@@ -2,7 +2,7 @@ require 'rails/source_annotation_extractor'
desc "Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)"
task :notes do
- SourceAnnotationExtractor.enumerate "OPTIMIZE|FIXME|TODO", :tag => true
+ SourceAnnotationExtractor.enumerate "OPTIMIZE|FIXME|TODO", tag: true
end
namespace :notes do
diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake
index f9cff5b627..50499304cb 100644
--- a/railties/lib/rails/tasks/framework.rake
+++ b/railties/lib/rails/tasks/framework.rake
@@ -1,6 +1,6 @@
namespace :rails do
desc "Update configs and some other initially generated files (or use just update:configs, update:scripts, or update:application_controller)"
- task :update => [ "update:configs", "update:scripts", "update:application_controller" ]
+ task update: [ "update:configs", "update:scripts", "update:application_controller" ]
desc "Applies the template supplied by LOCATION=(/path/to/template) or URL"
task :template do
@@ -9,8 +9,8 @@ namespace :rails do
template = File.expand_path(template) if template !~ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://}
require 'rails/generators'
require 'rails/generators/rails/app/app_generator'
- generator = Rails::Generators::AppGenerator.new [Rails.root], {}, :destination_root => Rails.root
- generator.apply template, :verbose => false
+ generator = Rails::Generators::AppGenerator.new [Rails.root], {}, destination_root: Rails.root
+ generator.apply template, verbose: false
end
namespace :templates do
@@ -44,8 +44,8 @@ namespace :rails do
@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
+ gen = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true },
+ destination_root: Rails.root
File.exists?(Rails.root.join("config", "application.rb")) ?
gen.send(:app_const) : gen.send(:valid_app_const?)
gen
diff --git a/railties/lib/rails/tasks/middleware.rake b/railties/lib/rails/tasks/middleware.rake
index cd35ffb19a..31e961b483 100644
--- a/railties/lib/rails/tasks/middleware.rake
+++ b/railties/lib/rails/tasks/middleware.rake
@@ -1,5 +1,5 @@
desc 'Prints out your Rack middleware stack'
-task :middleware => :environment do
+task middleware: :environment do
Rails.configuration.middleware.each do |middleware|
puts "use #{middleware.inspect}"
end
diff --git a/railties/lib/rails/tasks/misc.rake b/railties/lib/rails/tasks/misc.rake
index 0a9b9ff4ff..4195106961 100644
--- a/railties/lib/rails/tasks/misc.rake
+++ b/railties/lib/rails/tasks/misc.rake
@@ -5,7 +5,7 @@ task :secret do
end
desc 'List versions of all Rails frameworks and the environment'
-task :about => :environment do
+task about: :environment do
puts Rails::Info
end
@@ -26,7 +26,7 @@ namespace :time do
require 'active_support'
require 'active_support/time'
jan_offset = Time.now.beginning_of_year.utc_offset
- jul_offset = Time.now.beginning_of_year.change(:month => 7).utc_offset
+ jul_offset = Time.now.beginning_of_year.change(month: 7).utc_offset
offset = jan_offset < jul_offset ? jan_offset : jul_offset
build_time_zone_list(:all, offset)
end
diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake
index 95f47566ef..676b475640 100644
--- a/railties/lib/rails/tasks/routes.rake
+++ b/railties/lib/rails/tasks/routes.rake
@@ -1,5 +1,5 @@
desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.'
-task :routes => :environment do
+task routes: :environment do
all_routes = Rails.application.routes.routes
require 'action_dispatch/routing/inspector'
inspector = ActionDispatch::Routing::RoutesInspector.new
diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake
index 67a6d2d2ac..c1674c72ad 100644
--- a/railties/lib/rails/tasks/statistics.rake
+++ b/railties/lib/rails/tasks/statistics.rake
@@ -6,9 +6,13 @@ STATS_DIRECTORIES = [
%w(Javascripts app/assets/javascripts),
%w(Libraries lib/),
%w(APIs app/apis),
+ %w(Controller\ tests test/controllers),
+ %w(Helper\ tests test/helpers),
+ %w(Model\ tests test/models),
+ %w(Mailer\ tests test/mailers),
%w(Integration\ tests test/integration),
- %w(Functional\ tests test/functional),
- %w(Unit\ tests test/unit)
+ %w(Functional\ tests\ (old) test/functional),
+ %w(Unit\ tests \ (old) test/unit)
].collect { |name, dir| [ name, "#{Rails.root}/#{dir}" ] }.select { |name, dir| File.directory?(dir) }
desc "Report code statistics (KLOCs, etc) from the application"
diff --git a/railties/lib/rails/tasks/tmp.rake b/railties/lib/rails/tasks/tmp.rake
index 0bb64ced95..116988665f 100644
--- a/railties/lib/rails/tasks/tmp.rake
+++ b/railties/lib/rails/tasks/tmp.rake
@@ -1,6 +1,6 @@
namespace :tmp do
desc "Clear session, cache, and socket files from tmp/ (narrow w/ tmp:sessions:clear, tmp:cache:clear, tmp:sockets:clear)"
- task :clear => [ "tmp:sessions:clear", "tmp:cache:clear", "tmp:sockets:clear"]
+ task clear: [ "tmp:sessions:clear", "tmp:cache:clear", "tmp:sockets:clear"]
tmp_dirs = [ 'tmp/sessions',
'tmp/cache',
@@ -13,7 +13,7 @@ namespace :tmp do
tmp_dirs.each { |d| directory d }
desc "Creates tmp directories for sessions, cache, sockets, and pids"
- task :create => tmp_dirs
+ task create: tmp_dirs
namespace :sessions do
# desc "Clears all files in tmp/sessions"
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
index 581ceaf9ce..aed7fd4b14 100644
--- a/railties/lib/rails/test_help.rb
+++ b/railties/lib/rails/test_help.rb
@@ -7,6 +7,10 @@ require 'active_support/test_case'
require 'action_controller/test_case'
require 'action_dispatch/testing/integration'
+# Config Rails backtrace in tests.
+require 'rails/backtrace_cleaner'
+MiniTest.backtrace_filter = Rails.backtrace_cleaner
+
# Enable turn if it is available
begin
require 'turn'
@@ -25,8 +29,8 @@ if defined?(ActiveRecord::Base)
ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
- def create_fixtures(*table_names, &block)
- Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names, {}, &block)
+ def create_fixtures(*fixture_set_names, &block)
+ FixtureSet.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, {}, &block)
end
end
diff --git a/railties/lib/rails/test_unit/railtie.rb b/railties/lib/rails/test_unit/railtie.rb
index 2b6170ebfb..ed89ce4128 100644
--- a/railties/lib/rails/test_unit/railtie.rb
+++ b/railties/lib/rails/test_unit/railtie.rb
@@ -1,8 +1,8 @@
module Rails
class TestUnitRailtie < Rails::Railtie
config.app_generators do |c|
- c.test_framework :test_unit, :fixture => true,
- :fixture_replacement => nil
+ c.test_framework :test_unit, fixture: true,
+ fixture_replacement: nil
c.integration_tool :test_unit
c.performance_tool :test_unit
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 0de4afe905..cd59fbe599 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -15,11 +15,11 @@ def recent_tests(source_pattern, test_path, touched_since = 10.minutes.ago)
# Support subdirs in app/models and app/controllers
modified_test_path = source_dir.length > 2 ? "#{test_path}/" << source_dir[1..source_dir.length].join('/') : test_path
- # For modified files in app/ run the tests for it. ex. /test/functional/account_controller.rb
+ # For modified files in app/ run the tests for it. ex. /test/controllers/account_controller.rb
test = "#{modified_test_path}/#{source_file}_test.rb"
tests.push test if File.exist?(test)
- # For modified files in app, run tests in subdirs too. ex. /test/functional/account/*_test.rb
+ # For modified files in app, run tests in subdirs too. ex. /test/controllers/account/*_test.rb
test = "#{modified_test_path}/#{File.basename(path, '.rb').sub("_controller","")}"
FileList["#{test}/*_test.rb"].each { |f| tests.push f } if File.exist?(test)
@@ -43,7 +43,7 @@ module Kernel
end
end
-task :default => :test
+task default: :test
desc 'Runs test:units, test:functionals, test:integration together (also available: test:benchmark, test:profile)'
task :test do
@@ -61,7 +61,7 @@ namespace :test do
Rake::Task[task].invoke
nil
rescue => e
- { :task => task, :exception => e }
+ { task: task, exception: e }
end
end.compact
@@ -71,10 +71,12 @@ namespace :test do
end
end
- Rake::TestTask.new(:recent => "test:prepare") do |t|
+ Rake::TestTask.new(recent: "test:prepare") do |t|
since = TEST_CHANGES_SINCE
touched = FileList['test/**/*_test.rb'].select { |path| File.mtime(path) > since } +
+ recent_tests('app/models/**/*.rb', 'test/models', since) +
recent_tests('app/models/**/*.rb', 'test/unit', since) +
+ recent_tests('app/controllers/**/*.rb', 'test/controllers', since) +
recent_tests('app/controllers/**/*.rb', 'test/functional', since)
t.libs << 'test'
@@ -82,7 +84,7 @@ namespace :test do
end
Rake::Task['test:recent'].comment = "Test recent changes"
- Rake::TestTask.new(:uncommitted => "test:prepare") do |t|
+ Rake::TestTask.new(uncommitted: "test:prepare") do |t|
def t.file_list
if File.directory?(".svn")
changed_since_checkin = silence_stderr { `svn status` }.split.map { |path| path.chomp[7 .. -1] }
@@ -95,8 +97,10 @@ namespace :test do
models = changed_since_checkin.select { |path| path =~ /app[\\\/]models[\\\/].*\.rb$/ }
controllers = changed_since_checkin.select { |path| path =~ /app[\\\/]controllers[\\\/].*\.rb$/ }
- unit_tests = models.map { |model| "test/unit/#{File.basename(model, '.rb')}_test.rb" }
- functional_tests = controllers.map { |controller| "test/functional/#{File.basename(controller, '.rb')}_test.rb" }
+ unit_tests = models.map { |model| "test/models/#{File.basename(model, '.rb')}_test.rb" } +
+ models.map { |model| "test/unit/#{File.basename(model, '.rb')}_test.rb" } +
+ functional_tests = controllers.map { |controller| "test/controllers/#{File.basename(controller, '.rb')}_test.rb" } +
+ controllers.map { |controller| "test/functional/#{File.basename(controller, '.rb')}_test.rb" }
(unit_tests + functional_tests).uniq.select { |file| File.exist?(file) }
end
@@ -104,32 +108,52 @@ namespace :test do
end
Rake::Task['test:uncommitted'].comment = "Test changes since last checkin (only Subversion and Git)"
- Rake::TestTask.new(:single => "test:prepare") do |t|
+ Rake::TestTask.new(single: "test:prepare") do |t|
t.libs << "test"
end
- Rails::SubTestTask.new(:units => "test:prepare") do |t|
+ Rails::SubTestTask.new(models: "test:prepare") do |t|
t.libs << "test"
- t.pattern = 'test/unit/**/*_test.rb'
+ t.pattern = 'test/models/**/*_test.rb'
end
- Rails::SubTestTask.new(:functionals => "test:prepare") do |t|
+ Rails::SubTestTask.new(helpers: "test:prepare") do |t|
t.libs << "test"
- t.pattern = 'test/functional/**/*_test.rb'
+ t.pattern = 'test/helpers/**/*_test.rb'
end
- Rails::SubTestTask.new(:integration => "test:prepare") do |t|
+ Rails::SubTestTask.new(units: "test:prepare") do |t|
+ t.libs << "test"
+ t.pattern = 'test/{models,helpers,unit}/**/*_test.rb'
+ end
+
+ Rails::SubTestTask.new(controllers: "test:prepare") do |t|
+ t.libs << "test"
+ t.pattern = 'test/controllers/**/*_test.rb'
+ end
+
+ Rails::SubTestTask.new(mailers: "test:prepare") do |t|
+ t.libs << "test"
+ t.pattern = 'test/mailers/**/*_test.rb'
+ end
+
+ Rails::SubTestTask.new(functionals: "test:prepare") do |t|
+ t.libs << "test"
+ t.pattern = 'test/{controllers,mailers,functional}/**/*_test.rb'
+ end
+
+ Rails::SubTestTask.new(integration: "test:prepare") do |t|
t.libs << "test"
t.pattern = 'test/integration/**/*_test.rb'
end
- Rails::SubTestTask.new(:benchmark => 'test:prepare') do |t|
+ Rails::SubTestTask.new(benchmark: 'test:prepare') do |t|
t.libs << 'test'
t.pattern = 'test/performance/**/*_test.rb'
t.options = '-- --benchmark'
end
- Rails::SubTestTask.new(:profile => 'test:prepare') do |t|
+ Rails::SubTestTask.new(profile: 'test:prepare') do |t|
t.libs << 'test'
t.pattern = 'test/performance/**/*_test.rb'
end
diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb
index ecacb34cb2..1eddfac664 100644
--- a/railties/test/application/asset_debugging_test.rb
+++ b/railties/test/application/asset_debugging_test.rb
@@ -7,7 +7,7 @@ module ApplicationTests
include Rack::Test::Methods
def setup
- build_app(:initializers => true)
+ build_app(initializers: true)
app_file "app/assets/javascripts/application.js", "//= require_tree ."
app_file "app/assets/javascripts/xmlhr.js", "function f1() { alert(); }"
@@ -15,7 +15,7 @@ module ApplicationTests
app_file "config/routes.rb", <<-RUBY
AppTemplate::Application.routes.draw do
- get '/posts', :to => "posts#index"
+ get '/posts', to: "posts#index"
end
RUBY
@@ -36,9 +36,8 @@ module ApplicationTests
test "assets are concatenated when debug is off and compile is off either if debug_assets param is provided" do
# config.assets.debug and config.assets.compile are false for production environment
ENV["RAILS_ENV"] = "production"
- capture(:stdout) do
- Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
- end
+ output = Dir.chdir(app_path){ `bundle exec rake assets:precompile --trace 2>&1` }
+ assert $?.success?, output
require "#{app_path}/config/environment"
class ::PostsController < ActionController::Base ; end
@@ -50,7 +49,7 @@ module ApplicationTests
end
test "assets aren't concatened when compile is true is on and debug_assets params is true" do
- app_file "config/initializers/compile.rb", "Rails.application.config.assets.compile = true"
+ add_to_env_config "production", "config.assets.compile = true"
ENV["RAILS_ENV"] = "production"
require "#{app_path}/config/environment"
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index 12223287ba..1311194ea6 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
require 'isolation/abstract_unit'
require 'rack/test'
+require 'active_support/json'
module ApplicationTests
class AssetsTest < ActiveSupport::TestCase
@@ -8,7 +9,7 @@ module ApplicationTests
include Rack::Test::Methods
def setup
- build_app(:initializers => true)
+ build_app(initializers: true)
boot_rails
end
@@ -18,7 +19,7 @@ module ApplicationTests
def precompile!(env = nil)
quietly do
- precompile_task = "bundle exec rake assets:precompile #{env} 2>&1"
+ precompile_task = "bundle exec rake assets:precompile #{env} --trace 2>&1"
output = Dir.chdir(app_path) { %x[ #{precompile_task} ] }
assert $?.success?, output
output
@@ -27,12 +28,12 @@ module ApplicationTests
def clean_assets!
quietly do
- assert Dir.chdir(app_path) { system('bundle exec rake assets:clean') }
+ assert Dir.chdir(app_path) { system('bundle exec rake assets:clobber') }
end
end
def assert_file_exists(filename)
- assert File.exists?(filename), "missing #{filename}"
+ assert Dir[filename].first, "missing #{filename}"
end
def assert_no_file_exists(filename)
@@ -44,7 +45,7 @@ module ApplicationTests
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
- get '*path', :to => lambda { |env| [200, { "Content-Type" => "text/html" }, "Not an asset"] }
+ get '*path', to: lambda { |env| [200, { "Content-Type" => "text/html" }, "Not an asset"] }
end
RUBY
@@ -75,12 +76,10 @@ module ApplicationTests
precompile!
files = Dir["#{app_path}/public/assets/application-*.js"]
- files << Dir["#{app_path}/public/assets/application.js"].first
files << Dir["#{app_path}/public/assets/foo/application-*.js"].first
- files << Dir["#{app_path}/public/assets/foo/application.js"].first
files.each do |file|
assert_not_nil file, "Expected application.js asset to be generated, but none found"
- assert_equal "alert();", File.read(file)
+ assert_equal "alert();\n", File.read(file)
end
end
@@ -108,21 +107,26 @@ module ApplicationTests
precompile!
+ images_should_compile = ["a-*.png", "happyface-*.png", "happy_face-*.png", "happy.face-*.png",
+ "happy-face-*.png", "happy.happy_face-*.png", "happy_happy.face-*.png",
+ "happy.happy.face-*.png", "-happy-*.png", "-happy.face-*.png",
+ "_happy.face-*.png", "_happy-*.png"]
+
images_should_compile.each do |filename|
assert_file_exists("#{app_path}/public/assets/#{filename}")
end
- assert_file_exists("#{app_path}/public/assets/application.js")
- assert_file_exists("#{app_path}/public/assets/application.css")
+ assert_file_exists("#{app_path}/public/assets/application-*.js")
+ assert_file_exists("#{app_path}/public/assets/application-*.css")
- assert_no_file_exists("#{app_path}/public/assets/someapplication.js")
- assert_no_file_exists("#{app_path}/public/assets/someapplication.css")
+ assert_no_file_exists("#{app_path}/public/assets/someapplication-*.js")
+ assert_no_file_exists("#{app_path}/public/assets/someapplication-*.css")
- assert_no_file_exists("#{app_path}/public/assets/something.min.js")
- assert_no_file_exists("#{app_path}/public/assets/something.min.css")
+ assert_no_file_exists("#{app_path}/public/assets/something.min-*.js")
+ assert_no_file_exists("#{app_path}/public/assets/something.min-*.css")
- assert_no_file_exists("#{app_path}/public/assets/something.else.js")
- assert_no_file_exists("#{app_path}/public/assets/something.else.css")
+ assert_no_file_exists("#{app_path}/public/assets/something.else-*.js")
+ assert_no_file_exists("#{app_path}/public/assets/something.else-*.css")
end
test "precompile something.js for directory containing index file" do
@@ -131,7 +135,7 @@ module ApplicationTests
precompile!
- assert_file_exists("#{app_path}/public/assets/something.js")
+ assert_file_exists("#{app_path}/public/assets/something-*.js")
end
test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do
@@ -151,11 +155,11 @@ module ApplicationTests
add_to_config "config.assets.digest = true"
precompile!
- manifest = "#{app_path}/public/assets/manifest.yml"
+ manifest = Dir["#{app_path}/public/assets/manifest-*.json"].first
- assets = YAML.load_file(manifest)
- assert_match(/application-([0-z]+)\.js/, assets["application.js"])
- assert_match(/application-([0-z]+)\.css/, assets["application.css"])
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ assert_match(/application-([0-z]+)\.js/, assets["assets"]["application.js"])
+ assert_match(/application-([0-z]+)\.css/, assets["assets"]["application.css"])
end
test "the manifest file should be saved by default in the same assets folder" do
@@ -166,26 +170,9 @@ module ApplicationTests
precompile!
- manifest = "#{app_path}/public/x/manifest.yml"
- assets = YAML.load_file(manifest)
- assert_match(/application-([0-z]+)\.js/, assets["application.js"])
- end
-
- test "precompile does not append asset digests when config.assets.digest is false" do
- app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
- app_file "app/assets/javascripts/application.js", "alert();"
- add_to_config "config.assets.digest = false"
-
- precompile!
-
- assert_file_exists("#{app_path}/public/assets/application.js")
- assert_file_exists("#{app_path}/public/assets/application.css")
-
- manifest = "#{app_path}/public/assets/manifest.yml"
-
- assets = YAML.load_file(manifest)
- assert_equal "application.js", assets["application.js"]
- assert_equal "application.css", assets["application.css"]
+ manifest = Dir["#{app_path}/public/x/manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ assert_match(/application-([0-z]+)\.js/, assets["assets"]["application.js"])
end
test "assets do not require any assets group gem when manifest file is present" do
@@ -195,9 +182,9 @@ module ApplicationTests
ENV["RAILS_ENV"] = "production"
precompile!
- manifest = "#{app_path}/public/assets/manifest.yml"
- assets = YAML.load_file(manifest)
- asset_path = assets["application.js"]
+ manifest = Dir["#{app_path}/public/assets/manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ asset_path = assets["assets"]["application.js"]
require "#{app_path}/config/environment"
@@ -208,58 +195,6 @@ module ApplicationTests
assert !defined?(Uglifier)
end
- test "assets raise AssetNotPrecompiledError when manifest file is present and requested file isn't precompiled" do
- app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'app' %>"
-
- app_file "config/routes.rb", <<-RUBY
- AppTemplate::Application.routes.draw do
- get '/posts', :to => "posts#index"
- end
- RUBY
-
- ENV["RAILS_ENV"] = "production"
- precompile!
-
- # Create file after of precompile
- app_file "app/assets/javascripts/app.js", "alert();"
-
- require "#{app_path}/config/environment"
- class ::PostsController < ActionController::Base
- def show_detailed_exceptions?() true end
- end
-
- get '/posts'
- assert_match(/AssetNotPrecompiledError/, last_response.body)
- assert_match(/app\.js isn&#39;t precompiled/, last_response.body)
- end
-
- test "assets raise AssetNotPrecompiledError when manifest file is present and requested file isn't precompiled if digest is disabled" do
- app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'app' %>"
- add_to_config "config.assets.compile = false"
- add_to_config "config.assets.digest = false"
-
- app_file "config/routes.rb", <<-RUBY
- AppTemplate::Application.routes.draw do
- get '/posts', :to => "posts#index"
- end
- RUBY
-
- ENV["RAILS_ENV"] = "production"
- precompile!
-
- # Create file after of precompile
- app_file "app/assets/javascripts/app.js", "alert();"
-
- require "#{app_path}/config/environment"
- class ::PostsController < ActionController::Base
- def show_detailed_exceptions?() true end
- end
-
- get '/posts'
- assert_match(/AssetNotPrecompiledError/, last_response.body)
- assert_match(/app\.js isn&#39;t precompiled/, last_response.body)
- end
-
test "precompile properly refers files referenced with asset_path and and run in the provided RAILS_ENV" do
app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
# digest is default in false, we must enable it for test environment
@@ -267,33 +202,32 @@ module ApplicationTests
precompile!('RAILS_ENV=test')
- file = Dir["#{app_path}/public/assets/application.css"].first
- assert_match(/\/assets\/rails\.png/, File.read(file))
file = Dir["#{app_path}/public/assets/application-*.css"].first
assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file))
end
- test "precompile shouldn't use the digests present in manifest.yml" do
- app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
+ test "precompile shouldn't use the digests present in manifest.json" do
+ app_file "app/assets/stylesheets/application.css.erb", "//= depend_on rails.png\np { url: <%= asset_path('rails.png') %> }"
ENV["RAILS_ENV"] = "production"
precompile!
- manifest = "#{app_path}/public/assets/manifest.yml"
- assets = YAML.load_file(manifest)
- asset_path = assets["application.css"]
+ manifest = Dir["#{app_path}/public/assets/manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ asset_path = assets["assets"]["application.css"]
- app_file "app/assets/images/rails.png", "image changed"
+ app_file "app/assets/images/rails.png", "p { url: change }"
precompile!
- assets = YAML.load_file(manifest)
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
- assert_not_equal asset_path, assets["application.css"]
+ assert_not_equal asset_path, assets["assets"]["application.css"]
end
- test "precompile appends the md5 hash to files referenced with asset_path and run in production as default even using RAILS_GROUPS=assets" do
+ test "precompile appends the md5 hash to files referenced with asset_path and run in production with digest true" do
app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
add_to_config "config.assets.compile = true"
+ add_to_config "config.assets.digest = true"
ENV["RAILS_ENV"] = nil
@@ -309,11 +243,16 @@ module ApplicationTests
add_to_config "config.assets.precompile = [ /\.png$/, /application.(css|js)$/ ]"
precompile!
+
+ manifest = Dir["#{app_path}/public/assets/manifest-*.json"].first
+ assets = ActiveSupport::JSON.decode(File.read(manifest))
+ assert asset_path = assets["assets"].find { |(k, v)| k !~ /rails.png/ && k =~ /.png/ }[1]
+
require "#{app_path}/config/environment"
- get "/assets/#{URI.parser.escape(filename)}"
+ get "/assets/#{URI.parser.escape(asset_path)}"
assert_match "not a image really", last_response.body
- assert_file_exists("#{app_path}/public/assets/#{filename}")
+ assert_file_exists("#{app_path}/public/assets/#{asset_path}")
end
test "assets are cleaned up properly" do
@@ -353,7 +292,7 @@ module ApplicationTests
class ::OmgController < ActionController::Base
def index
flash[:cool_story] = true
- render :text => "ok"
+ render text: "ok"
end
end
@@ -396,21 +335,6 @@ module ApplicationTests
assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"><\/script>/, last_response.body)
end
- test "assets aren't concatened when compile is true is on and debug_assets params is true" do
- app_with_assets_in_view
- add_to_env_config "production", "config.assets.compile = true"
- add_to_env_config "production", "config.assets.allow_debugging = true"
-
- ENV["RAILS_ENV"] = "production"
- require "#{app_path}/config/environment"
-
- class ::PostsController < ActionController::Base ; end
-
- get '/posts?debug_assets=true'
- assert_match(/<script src="\/assets\/application-([0-z]+)\.js\?body=1"><\/script>/, last_response.body)
- assert_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js\?body=1"><\/script>/, last_response.body)
- end
-
test "assets can access model information when precompiling" do
app_file "app/models/post.rb", "class Post; end"
app_file "app/assets/javascripts/application.js", "//= require_tree ."
@@ -418,7 +342,7 @@ module ApplicationTests
add_to_config "config.assets.digest = false"
precompile!
- assert_equal "Post;\n", File.read("#{app_path}/public/assets/application.js")
+ assert_equal "Post;\n", File.read(Dir["#{app_path}/public/assets/application-*.js"].first)
end
test "assets can't access model information when precompiling if not initializing the app" do
@@ -430,7 +354,7 @@ module ApplicationTests
add_to_config "config.assets.initialize_on_precompile = false"
precompile!
- assert_equal "NoPost;\n", File.read("#{app_path}/public/assets/application.js")
+ assert_equal "NoPost;\n", File.read(Dir["#{app_path}/public/assets/application-*.js"].first)
end
test "initialization on the assets group should set assets_dir" do
@@ -464,7 +388,7 @@ module ApplicationTests
clean_assets!
- files = Dir["#{app_path}/public/production_assets/application.js"]
+ files = Dir["#{app_path}/public/production_assets/application-*.js"]
assert_equal 0, files.length, "Expected application.js asset to be removed, but still exists"
end
@@ -486,7 +410,7 @@ module ApplicationTests
add_to_config "config.asset_host = 'example.com'"
precompile!
- assert_match 'src="//example.com/assets/rails.png"', File.read("#{app_path}/public/assets/image_loader.js")
+ assert_match 'src="//example.com/assets/rails.png"', File.read(Dir["#{app_path}/public/assets/image_loader-*.js"].first)
end
test "asset paths should use RAILS_RELATIVE_URL_ROOT by default" do
@@ -496,19 +420,7 @@ module ApplicationTests
add_to_config "config.assets.precompile = %w{app.js}"
precompile!
- assert_match 'src="/sub/uri/assets/rails.png"', File.read("#{app_path}/public/assets/app.js")
- end
-
- test "html assets are compiled when executing precompile" do
- app_file "app/assets/pages/page.html.erb", "<%= javascript_include_tag :application %>"
- ENV["RAILS_ENV"] = "production"
- ENV["RAILS_GROUP"] = "assets"
-
- quietly do
- Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
- end
-
- assert_file_exists("#{app_path}/public/assets/page.html")
+ assert_match 'src="/sub/uri/assets/rails.png"', File.read(Dir["#{app_path}/public/assets/app-*.js"].first)
end
test "assets:cache:clean should clean cache" do
@@ -516,11 +428,10 @@ module ApplicationTests
precompile!
quietly do
- Dir.chdir(app_path){ `bundle exec rake assets:cache:clean` }
+ Dir.chdir(app_path){ `bundle exec rake assets:clobber` }
end
- require "#{app_path}/config/environment"
- assert_equal 0, Dir.entries(Rails.application.assets.cache.cache_path).size - 2 # reject [".", ".."]
+ assert !File.exist?("#{app_path}/tmp/cache/assets")
end
private
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index d014e5e362..c4c1100f19 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -56,14 +56,6 @@ module ApplicationTests
assert_match "ActiveRecord::PendingMigrationError", last_response.body
end
- test "multiple queue construction is possible" do
- require 'rails'
- require "#{app_path}/config/environment"
- mail_queue = Rails.application.build_queue
- image_processing_queue = Rails.application.build_queue
- assert_not_equal mail_queue, image_processing_queue
- end
-
test "Rails.groups returns available groups" do
require "rails"
@@ -139,6 +131,14 @@ module ApplicationTests
assert_instance_of Pathname, Rails.root
end
+ test "Rails.public_path should be a Pathname" do
+ add_to_config <<-RUBY
+ config.paths["public"] = "somewhere"
+ RUBY
+ require "#{app_path}/config/environment"
+ assert_instance_of Pathname, Rails.public_path
+ end
+
test "initialize an eager loaded, cache classes app" do
add_to_config <<-RUBY
config.eager_load = true
@@ -154,11 +154,6 @@ module ApplicationTests
assert AppTemplate::Application, AppTemplate::Application.config.eager_load_namespaces
end
- test "asset_path defaults to nil for application" do
- require "#{app_path}/config/environment"
- assert_equal nil, AppTemplate::Application.config.asset_path
- end
-
test "the application can be eager loaded even when there are no frameworks" do
FileUtils.rm_rf("#{app_path}/config/environments")
add_to_config <<-RUBY
@@ -227,7 +222,7 @@ module ApplicationTests
RUBY
require "#{app_path}/config/application"
- assert_equal File.join(app_path, "somewhere"), Rails.public_path
+ assert_equal Pathname.new(app_path).join("somewhere"), Rails.public_path
end
test "config.secret_token is sent in env" do
@@ -239,7 +234,7 @@ module ApplicationTests
class ::OmgController < ActionController::Base
def index
cookies.signed[:some_key] = "some_value"
- render :text => env["action_dispatch.secret_token"]
+ render text: env["action_dispatch.secret_token"]
end
end
@@ -252,7 +247,7 @@ module ApplicationTests
class ::OmgController < ActionController::Base
def index
- render :inline => "<%= csrf_meta_tags %>"
+ render inline: "<%= csrf_meta_tags %>"
end
end
@@ -272,11 +267,11 @@ module ApplicationTests
app_file 'app/controllers/posts_controller.rb', <<-RUBY
class PostsController < ApplicationController
def show
- render :inline => "<%= begin; form_for(Post.new) {}; rescue => e; e.to_s; end %>"
+ render inline: "<%= begin; form_for(Post.new) {}; rescue => e; e.to_s; end %>"
end
def update
- render :text => "update"
+ render text: "update"
end
end
RUBY
@@ -291,7 +286,7 @@ module ApplicationTests
token = "cf50faa3fe97702ca1ae"
PostsController.any_instance.stubs(:form_authenticity_token).returns(token)
- params = {:authenticity_token => token}
+ params = {authenticity_token: token}
get "/posts/1"
assert_match(/patch/, last_response.body)
@@ -316,7 +311,7 @@ module ApplicationTests
class ::OmgController < ActionController::Base
def index
- render :inline => "<%= csrf_meta_tags %>"
+ render inline: "<%= csrf_meta_tags %>"
end
end
@@ -324,27 +319,6 @@ module ApplicationTests
assert last_response.body =~ /_xsrf_token_here/
end
- test "config.action_controller.perform_caching = true" do
- make_basic_app do |app|
- app.config.action_controller.perform_caching = true
- end
-
- class ::OmgController < ActionController::Base
- @@count = 0
-
- caches_action :index
- def index
- @@count += 1
- render :text => @@count
- end
- end
-
- get "/"
- res = last_response.body
- get "/"
- assert_equal res, last_response.body # value should be unchanged
- end
-
test "sets ActionDispatch.test_app" do
make_basic_app
assert_equal Rails.application, ActionDispatch.test_app
@@ -454,42 +428,6 @@ module ApplicationTests
end
end
- test "config.action_controller.perform_caching = false" do
- make_basic_app do |app|
- app.config.action_controller.perform_caching = false
- end
-
- class ::OmgController < ActionController::Base
- @@count = 0
-
- caches_action :index
- def index
- @@count += 1
- render :text => @@count
- end
- end
-
- get "/"
- res = last_response.body
- get "/"
- assert_not_equal res, last_response.body
- end
-
- test "config.asset_path is not passed through env" do
- make_basic_app do |app|
- app.config.asset_path = "/omg%s"
- end
-
- class ::OmgController < ActionController::Base
- def index
- render :inline => "<%= image_path('foo.jpg') %>"
- end
- end
-
- get "/"
- assert_equal "/omg/images/foo.jpg", last_response.body
- end
-
test "config.action_view.cache_template_loading with cache_classes default" do
add_to_config "config.cache_classes = true"
require "#{app_path}/config/environment"
@@ -535,7 +473,7 @@ module ApplicationTests
class ::OmgController < ActionController::Base
def index
- render :text => env["action_dispatch.show_exceptions"]
+ render text: env["action_dispatch.show_exceptions"]
end
end
@@ -545,7 +483,7 @@ module ApplicationTests
test "config.action_controller.wrap_parameters is set in ActionController::Base" do
app_file 'config/initializers/wrap_parameters.rb', <<-RUBY
- ActionController::Base.wrap_parameters :format => [:json]
+ ActionController::Base.wrap_parameters format: [:json]
RUBY
app_file 'app/models/post.rb', <<-RUBY
@@ -565,7 +503,7 @@ module ApplicationTests
app_file 'app/controllers/posts_controller.rb', <<-RUBY
class PostsController < ApplicationController
def create
- render :text => params[:post].inspect
+ render text: params[:post].inspect
end
end
RUBY
@@ -586,7 +524,7 @@ module ApplicationTests
app_file 'app/controllers/posts_controller.rb', <<-RUBY
class PostsController < ActionController::Base
def create
- render :text => params[:post].permitted? ? "permitted" : "forbidden"
+ render text: params[:post].permitted? ? "permitted" : "forbidden"
end
end
RUBY
@@ -600,7 +538,7 @@ module ApplicationTests
require "#{app_path}/config/environment"
- post "/posts", {:post => {"title" =>"zomg"}}
+ post "/posts", {post: {"title" =>"zomg"}}
assert_equal 'permitted', last_response.body
end
@@ -612,8 +550,8 @@ module ApplicationTests
class ::OmgController < ActionController::Base
def index
respond_to do |format|
- format.html { render :text => "HTML" }
- format.xml { render :text => "XML" }
+ format.html { render text: "HTML" }
+ format.xml { render text: "XML" }
end
end
end
@@ -621,7 +559,7 @@ module ApplicationTests
get "/", {}, "HTTP_ACCEPT" => "application/xml"
assert_equal 'HTML', last_response.body
- get "/", { :format => :xml }, "HTTP_ACCEPT" => "application/xml"
+ get "/", { format: :xml }, "HTTP_ACCEPT" => "application/xml"
assert_equal 'XML', last_response.body
end
@@ -634,6 +572,7 @@ module ApplicationTests
assert_equal app.env_config['action_dispatch.show_exceptions'], app.config.action_dispatch.show_exceptions
assert_equal app.env_config['action_dispatch.logger'], Rails.logger
assert_equal app.env_config['action_dispatch.backtrace_cleaner'], Rails.backtrace_cleaner
+ assert_equal app.env_config['action_dispatch.key_generator'], Rails.application.key_generator
end
test "config.colorize_logging default is true" do
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
index b80244f1d2..bc0af499c1 100644
--- a/railties/test/application/generators_test.rb
+++ b/railties/test/application/generators_test.rb
@@ -48,22 +48,22 @@ module ApplicationTests
c.generators.orm = :data_mapper
c.generators.test_framework = :rspec
c.generators.helper = false
- expected = { :rails => { :orm => :data_mapper, :test_framework => :rspec, :helper => false } }
+ expected = { rails: { orm: :data_mapper, test_framework: :rspec, helper: false } }
assert_equal(expected, c.generators.options)
end
end
test "generators set rails aliases" do
with_config do |c|
- c.generators.aliases = { :rails => { :test_framework => "-w" } }
- expected = { :rails => { :test_framework => "-w" } }
+ c.generators.aliases = { rails: { test_framework: "-w" } }
+ expected = { rails: { test_framework: "-w" } }
assert_equal expected, c.generators.aliases
end
end
test "generators aliases, options, templates and fallbacks on initialization" do
add_to_config <<-RUBY
- config.generators.rails :aliases => { :test_framework => "-w" }
+ config.generators.rails aliases: { test_framework: "-w" }
config.generators.orm :data_mapper
config.generators.test_framework :rspec
config.generators.fallbacks[:shoulda] = :test_unit
@@ -76,7 +76,7 @@ module ApplicationTests
assert_equal :rspec, Rails::Generators.options[:rails][:test_framework]
assert_equal "-w", Rails::Generators.aliases[:rails][:test_framework]
- assert_equal Hash[:shoulda => :test_unit], Rails::Generators.fallbacks
+ assert_equal Hash[shoulda: :test_unit], Rails::Generators.fallbacks
assert_equal ["some/where"], Rails::Generators.templates_path
end
@@ -95,31 +95,31 @@ module ApplicationTests
test "generators with hashes for options and aliases" do
with_bare_config do |c|
c.generators do |g|
- g.orm :data_mapper, :migration => false
- g.plugin :aliases => { :generator => "-g" },
- :generator => true
+ g.orm :data_mapper, migration: false
+ g.plugin aliases: { generator: "-g" },
+ generator: true
end
expected = {
- :rails => { :orm => :data_mapper },
- :plugin => { :generator => true },
- :data_mapper => { :migration => false }
+ rails: { orm: :data_mapper },
+ plugin: { generator: true },
+ data_mapper: { migration: false }
}
assert_equal expected, c.generators.options
- assert_equal({ :plugin => { :generator => "-g" } }, c.generators.aliases)
+ assert_equal({ plugin: { generator: "-g" } }, c.generators.aliases)
end
end
test "generators with string and hash for options should generate symbol keys" do
with_bare_config do |c|
c.generators do |g|
- g.orm 'data_mapper', :migration => false
+ g.orm 'data_mapper', migration: false
end
expected = {
- :rails => { :orm => :data_mapper },
- :data_mapper => { :migration => false }
+ rails: { orm: :data_mapper },
+ data_mapper: { migration: false }
}
assert_equal expected, c.generators.options
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 5268257d62..528bec58ef 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -52,14 +52,13 @@ module ApplicationTests
test "uses the default queue for ActionMailer" do
require "#{app_path}/config/environment"
- assert_kind_of ActiveSupport::QueueContainer, ActionMailer::Base.queue
+ assert_kind_of ActiveSupport::Queue, ActionMailer::Base.queue
end
test "allows me to configure queue for ActionMailer" do
app_file "config/environments/development.rb", <<-RUBY
AppTemplate::Application.configure do
- Rails.queue[:mailer] = ActiveSupport::TestQueue.new
- config.action_mailer.queue = Rails.queue[:mailer]
+ config.action_mailer.queue = ActiveSupport::TestQueue.new
end
RUBY
@@ -175,7 +174,7 @@ module ApplicationTests
Dir.chdir("#{app_path}/app") do
require "#{app_path}/config/environment"
- assert_raises(NoMethodError) { [1,2,3].forty_two }
+ assert_raises(NoMethodError) { "hello".exclude? "lo" }
end
end
diff --git a/railties/test/application/initializers/i18n_test.rb b/railties/test/application/initializers/i18n_test.rb
index 02d20bc150..489b7ddb92 100644
--- a/railties/test/application/initializers/i18n_test.rb
+++ b/railties/test/application/initializers/i18n_test.rb
@@ -143,7 +143,7 @@ en:
I18n::Railtie.config.i18n.fallbacks = true
load_app
assert I18n.backend.class.included_modules.include?(I18n::Backend::Fallbacks)
- assert_fallbacks :de => [:de, :en]
+ assert_fallbacks de: [:de, :en]
end
test "config.i18n.fallbacks = true initializes I18n.fallbacks with default settings even when backend changes" do
@@ -151,37 +151,37 @@ en:
I18n::Railtie.config.i18n.backend = Class.new(I18n::Backend::Simple).new
load_app
assert I18n.backend.class.included_modules.include?(I18n::Backend::Fallbacks)
- assert_fallbacks :de => [:de, :en]
+ assert_fallbacks de: [:de, :en]
end
test "config.i18n.fallbacks.defaults = [:'en-US'] initializes fallbacks with en-US as a fallback default" do
I18n::Railtie.config.i18n.fallbacks.defaults = [:'en-US']
load_app
- assert_fallbacks :de => [:de, :'en-US', :en]
+ assert_fallbacks de: [:de, :'en-US', :en]
end
test "config.i18n.fallbacks.map = { :ca => :'es-ES' } initializes fallbacks with a mapping ca => es-ES" do
I18n::Railtie.config.i18n.fallbacks.map = { :ca => :'es-ES' }
load_app
- assert_fallbacks :ca => [:ca, :"es-ES", :es, :en]
+ assert_fallbacks ca: [:ca, :"es-ES", :es, :en]
end
test "[shortcut] config.i18n.fallbacks = [:'en-US'] initializes fallbacks with en-US as a fallback default" do
I18n::Railtie.config.i18n.fallbacks = [:'en-US']
load_app
- assert_fallbacks :de => [:de, :'en-US', :en]
+ assert_fallbacks de: [:de, :'en-US', :en]
end
test "[shortcut] config.i18n.fallbacks = [{ :ca => :'es-ES' }] initializes fallbacks with a mapping de-AT => de-DE" do
I18n::Railtie.config.i18n.fallbacks.map = { :ca => :'es-ES' }
load_app
- assert_fallbacks :ca => [:ca, :"es-ES", :es, :en]
+ assert_fallbacks ca: [:ca, :"es-ES", :es, :en]
end
test "[shortcut] config.i18n.fallbacks = [:'en-US', { :ca => :'es-ES' }] initializes fallbacks with the given arguments" do
I18n::Railtie.config.i18n.fallbacks = [:'en-US', { :ca => :'es-ES' }]
load_app
- assert_fallbacks :ca => [:ca, :"es-ES", :es, :'en-US', :en]
+ assert_fallbacks ca: [:ca, :"es-ES", :es, :'en-US', :en]
end
end
end
diff --git a/railties/test/application/initializers/notifications_test.rb b/railties/test/application/initializers/notifications_test.rb
index d866a63fe0..baae6fd928 100644
--- a/railties/test/application/initializers/notifications_test.rb
+++ b/railties/test/application/initializers/notifications_test.rb
@@ -33,7 +33,7 @@ module ApplicationTests
ActiveRecord::Base.logger = logger
# Mimic Active Record notifications
- instrument "sql.active_record", :name => "SQL", :sql => "SHOW tables"
+ instrument "sql.active_record", name: "SQL", sql: "SHOW tables"
wait
assert_equal 1, logger.logged(:debug).size
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
index fcbc3c048c..ad7172c514 100644
--- a/railties/test/application/loading_test.rb
+++ b/railties/test/application/loading_test.rb
@@ -19,14 +19,14 @@ class LoadingTest < ActiveSupport::TestCase
test "constants in app are autoloaded" do
app_file "app/models/post.rb", <<-MODEL
class Post < ActiveRecord::Base
- validates_acceptance_of :title, :accept => "omg"
+ validates_acceptance_of :title, accept: "omg"
end
MODEL
require "#{rails_root}/config/environment"
setup_ar!
- p = Post.create(:title => 'omg')
+ p = Post.create(title: 'omg')
assert_equal 1, Post.count
assert_equal 'omg', p.title
p = Post.first
@@ -36,7 +36,7 @@ class LoadingTest < ActiveSupport::TestCase
test "models without table do not panic on scope definitions when loaded" do
app_file "app/models/user.rb", <<-MODEL
class User < ActiveRecord::Base
- default_scope where(:published => true)
+ default_scope where(published: true)
end
MODEL
@@ -76,8 +76,8 @@ class LoadingTest < ActiveSupport::TestCase
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
- get '/load', :to => lambda { |env| [200, {}, Post.all] }
- get '/unload', :to => lambda { |env| [200, {}, []] }
+ get '/load', to: lambda { |env| [200, {}, Post.all] }
+ get '/unload', to: lambda { |env| [200, {}, []] }
end
RUBY
@@ -106,7 +106,7 @@ class LoadingTest < ActiveSupport::TestCase
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
- get '/c', :to => lambda { |env| [200, {"Content-Type" => "text/plain"}, [User.counter.to_s]] }
+ get '/c', to: lambda { |env| [200, {"Content-Type" => "text/plain"}, [User.counter.to_s]] }
end
RUBY
@@ -145,7 +145,7 @@ class LoadingTest < ActiveSupport::TestCase
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
- get '/c', :to => lambda { |env| [200, {"Content-Type" => "text/plain"}, [User.counter.to_s]] }
+ get '/c', to: lambda { |env| [200, {"Content-Type" => "text/plain"}, [User.counter.to_s]] }
end
RUBY
@@ -181,7 +181,7 @@ class LoadingTest < ActiveSupport::TestCase
app_file 'config/routes.rb', <<-RUBY
$counter = 0
AppTemplate::Application.routes.draw do
- get '/c', :to => lambda { |env| User; [200, {"Content-Type" => "text/plain"}, [$counter.to_s]] }
+ get '/c', to: lambda { |env| User; [200, {"Content-Type" => "text/plain"}, [$counter.to_s]] }
end
RUBY
@@ -212,8 +212,8 @@ class LoadingTest < ActiveSupport::TestCase
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
- get '/title', :to => lambda { |env| [200, {"Content-Type" => "text/plain"}, [Post.new.title]] }
- get '/body', :to => lambda { |env| [200, {"Content-Type" => "text/plain"}, [Post.new.body]] }
+ get '/title', to: lambda { |env| [200, {"Content-Type" => "text/plain"}, [Post.new.title]] }
+ get '/body', to: lambda { |env| [200, {"Content-Type" => "text/plain"}, [Post.new.body]] }
end
RUBY
@@ -229,7 +229,7 @@ class LoadingTest < ActiveSupport::TestCase
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
- t.string :title, :default => "TITLE"
+ t.string :title, default: "TITLE"
end
end
end
@@ -244,7 +244,7 @@ class LoadingTest < ActiveSupport::TestCase
app_file "db/migrate/2_add_body_to_posts.rb", <<-MIGRATION
class AddBodyToPosts < ActiveRecord::Migration
def change
- add_column :posts, :body, :text, :default => "BODY"
+ add_column :posts, :body, :text, default: "BODY"
end
end
MIGRATION
@@ -297,9 +297,9 @@ class LoadingTest < ActiveSupport::TestCase
protected
def setup_ar!
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Migration.verbose = false
- ActiveRecord::Schema.define(:version => 1) do
+ ActiveRecord::Schema.define(version: 1) do
create_table :posts do |t|
t.string :title
end
diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb
index 19d3f3a397..c99666d7e4 100644
--- a/railties/test/application/middleware/cache_test.rb
+++ b/railties/test/application/middleware/cache_test.rb
@@ -19,17 +19,17 @@ module ApplicationTests
controller :expires, <<-RUBY
class ExpiresController < ApplicationController
def expires_header
- expires_in 10, :public => !params[:private]
- render :text => SecureRandom.hex(16)
+ expires_in 10, public: !params[:private]
+ render text: SecureRandom.hex(16)
end
def expires_etag
- render_conditionally(:etag => "1")
+ render_conditionally(etag: "1")
end
def expires_last_modified
$last_modified ||= Time.now.utc
- render_conditionally(:last_modified => $last_modified)
+ render_conditionally(last_modified: $last_modified)
end
def keeps_if_modified_since
@@ -37,8 +37,8 @@ module ApplicationTests
end
private
def render_conditionally(headers)
- if stale?(headers.merge(:public => !params[:private]))
- render :text => SecureRandom.hex(16)
+ if stale?(headers.merge(public: !params[:private]))
+ render text: SecureRandom.hex(16)
end
end
end
@@ -78,6 +78,8 @@ module ApplicationTests
def test_cache_works_with_expires
simple_controller
+ add_to_config "config.action_dispatch.rack_cache = true"
+
get "/expires/expires_header"
assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
assert_equal "max-age=10, public", last_response.headers["Cache-Control"]
@@ -94,13 +96,15 @@ module ApplicationTests
def test_cache_works_with_expires_private
simple_controller
- get "/expires/expires_header", :private => true
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_header", private: true
assert_equal "miss", last_response.headers["X-Rack-Cache"]
assert_equal "private, max-age=10", last_response.headers["Cache-Control"]
body = last_response.body
- get "/expires/expires_header", :private => true
+ get "/expires/expires_header", private: true
assert_equal "miss", last_response.headers["X-Rack-Cache"]
assert_not_equal body, last_response.body
end
@@ -108,6 +112,8 @@ module ApplicationTests
def test_cache_works_with_etags
simple_controller
+ add_to_config "config.action_dispatch.rack_cache = true"
+
get "/expires/expires_etag"
assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
assert_equal "public", last_response.headers["Cache-Control"]
@@ -123,14 +129,16 @@ module ApplicationTests
def test_cache_works_with_etags_private
simple_controller
- get "/expires/expires_etag", :private => true
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_etag", private: true
assert_equal "miss", last_response.headers["X-Rack-Cache"]
assert_equal "must-revalidate, private, max-age=0", last_response.headers["Cache-Control"]
body = last_response.body
etag = last_response.headers["ETag"]
- get "/expires/expires_etag", {:private => true}, "If-None-Match" => etag
+ get "/expires/expires_etag", {private: true}, "If-None-Match" => etag
assert_equal "miss", last_response.headers["X-Rack-Cache"]
assert_not_equal body, last_response.body
end
@@ -138,6 +146,8 @@ module ApplicationTests
def test_cache_works_with_last_modified
simple_controller
+ add_to_config "config.action_dispatch.rack_cache = true"
+
get "/expires/expires_last_modified"
assert_equal "miss, store", last_response.headers["X-Rack-Cache"]
assert_equal "public", last_response.headers["Cache-Control"]
@@ -153,14 +163,16 @@ module ApplicationTests
def test_cache_works_with_last_modified_private
simple_controller
- get "/expires/expires_last_modified", :private => true
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ get "/expires/expires_last_modified", private: true
assert_equal "miss", last_response.headers["X-Rack-Cache"]
assert_equal "must-revalidate, private, max-age=0", last_response.headers["Cache-Control"]
body = last_response.body
last = last_response.headers["Last-Modified"]
- get "/expires/expires_last_modified", {:private => true}, "If-Modified-Since" => last
+ get "/expires/expires_last_modified", {private: true}, "If-Modified-Since" => last
assert_equal "miss", last_response.headers["X-Rack-Cache"]
assert_not_equal body, last_response.body
end
diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb
index 06dec81d40..5ce41caf61 100644
--- a/railties/test/application/middleware/session_test.rb
+++ b/railties/test/application/middleware/session_test.rb
@@ -36,7 +36,7 @@ module ApplicationTests
flash[:notice] = "notice"
end
- render :nothing => true
+ render nothing: true
end
end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 3e096e99f2..c5a0d02fe1 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -56,8 +56,14 @@ module ApplicationTests
assert !middleware.include?("Rack::Sendfile"), "Rack::Sendfile is not included in the default stack unless you set config.action_dispatch.x_sendfile_header"
end
- test "Rack::Cache is present when action_controller.perform_caching is set" do
- add_to_config "config.action_controller.perform_caching = true"
+ test "Rack::Cache is not included by default" do
+ boot!
+
+ assert !middleware.include?("Rack::Cache"), "Rack::Cache is not included in the default stack unless you set config.action_dispatch.rack_cache"
+ end
+
+ test "Rack::Cache is present when action_dispatch.rack_cache is set" do
+ add_to_config "config.action_dispatch.rack_cache = true"
boot!
@@ -72,10 +78,10 @@ module ApplicationTests
test "ActionDispatch::SSL is configured with options when given" do
add_to_config "config.force_ssl = true"
- add_to_config "config.ssl_options = { :host => 'example.com' }"
+ add_to_config "config.ssl_options = { host: 'example.com' }"
boot!
- assert_equal AppTemplate::Application.middleware.first.args, [{:host => 'example.com'}]
+ assert_equal AppTemplate::Application.middleware.first.args, [{host: 'example.com'}]
end
test "removing Active Record omits its middleware" do
@@ -160,9 +166,9 @@ module ApplicationTests
class ::OmgController < ActionController::Base
def index
if params[:nothing]
- render :text => ""
+ render text: ""
else
- render :text => "OMG"
+ render text: "OMG"
end
end
end
diff --git a/railties/test/application/queue_test.rb b/railties/test/application/queue_test.rb
index e67c6cc371..b0b3cf18e9 100644
--- a/railties/test/application/queue_test.rb
+++ b/railties/test/application/queue_test.rb
@@ -17,16 +17,16 @@ module ApplicationTests
@app_const ||= Class.new(Rails::Application)
end
- test "the queue is a TestQueue in test mode" do
+ test "the queue is a SynchronousQueue in test mode" do
app("test")
- assert_kind_of ActiveSupport::TestQueue, Rails.application.queue[:default]
- assert_kind_of ActiveSupport::TestQueue, Rails.queue[:default]
+ assert_kind_of ActiveSupport::SynchronousQueue, Rails.application.queue
+ assert_kind_of ActiveSupport::SynchronousQueue, Rails.queue
end
test "the queue is a SynchronousQueue in development mode" do
app("development")
- assert_kind_of ActiveSupport::SynchronousQueue, Rails.application.queue[:default]
- assert_kind_of ActiveSupport::SynchronousQueue, Rails.queue[:default]
+ assert_kind_of ActiveSupport::SynchronousQueue, Rails.application.queue
+ assert_kind_of ActiveSupport::SynchronousQueue, Rails.queue
end
class ThreadTrackingJob
@@ -58,54 +58,26 @@ module ApplicationTests
refute job.ran_in_different_thread?, "Expected job to run in the same thread"
end
- test "in test mode, explicitly draining the queue will process it in the same thread" do
+ test "in test mode, an enqueued job will be processed in the same thread" do
app("test")
- Rails.queue.push ThreadTrackingJob.new
- job = Rails.queue.jobs.last
- Rails.queue.drain
+ job = ThreadTrackingJob.new
+ Rails.queue.push job
+ sleep 0.1
assert job.ran?, "Expected job to be run"
refute job.ran_in_different_thread?, "Expected job to run in the same thread"
end
- class IdentifiableJob
- def initialize(id)
- @id = id
- end
-
- def ==(other)
- other.same_id?(@id)
- end
-
- def same_id?(other_id)
- other_id == @id
- end
-
- def run
- end
- end
-
- test "in test mode, the queue can be observed" do
- app("test")
-
- jobs = (1..10).map do |id|
- IdentifiableJob.new(id)
- end
+ test "in production, automatically spawn a queue consumer in a background thread" do
+ add_to_env_config "production", <<-RUBY
+ config.queue = ActiveSupport::Queue.new
+ RUBY
- jobs.each do |job|
- Rails.queue.push job
- end
+ app("production")
- assert_equal jobs, Rails.queue.jobs
- end
-
- test "in test mode, adding an unmarshallable job will raise an exception" do
- app("test")
- anonymous_class_instance = Struct.new(:run).new
- assert_raises TypeError do
- Rails.queue.push anonymous_class_instance
- end
+ assert_nil Rails.application.config.queue_consumer
+ assert_kind_of ActiveSupport::ThreadedQueueConsumer, Rails.application.queue_consumer
end
test "attempting to marshal a queue will raise an exception" do
@@ -115,18 +87,10 @@ module ApplicationTests
end
end
- test "attempting to add a reference to itself to the queue will raise an exception" do
- app("test")
- job = {reference: Rails.queue}
- assert_raises TypeError do
- Rails.queue.push job
- end
- end
-
def setup_custom_queue
add_to_env_config "production", <<-RUBY
require "my_queue"
- config.queue = MyQueue
+ config.queue = MyQueue.new
RUBY
app_file "lib/my_queue.rb", <<-RUBY
@@ -143,7 +107,7 @@ module ApplicationTests
test "a custom queue implementation can be provided" do
setup_custom_queue
- assert_kind_of MyQueue, Rails.queue[:default]
+ assert_kind_of MyQueue, Rails.queue
job = Struct.new(:id, :ran) do
def run
@@ -160,17 +124,16 @@ module ApplicationTests
test "a custom consumer implementation can be provided" do
add_to_env_config "production", <<-RUBY
require "my_queue_consumer"
- config.queue = ActiveSupport::Queue
- config.queue_consumer = MyQueueConsumer
+ config.queue = ActiveSupport::Queue.new
+ config.queue_consumer = MyQueueConsumer.new
RUBY
app_file "lib/my_queue_consumer.rb", <<-RUBY
- class MyQueueConsumer < ActiveSupport::ThreadedQueueConsumer
+ class MyQueueConsumer
attr_reader :started
def start
@started = true
- self
end
end
RUBY
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
index 27de75b63b..7a227098ba 100644
--- a/railties/test/application/rake/notes_test.rb
+++ b/railties/test/application/rake/notes_test.rb
@@ -25,6 +25,7 @@ module ApplicationTests
app_file "app/assets/stylesheets/application.css", "// TODO: note in css"
app_file "app/assets/stylesheets/application.css.scss", "// TODO: note in scss"
app_file "app/controllers/application_controller.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in ruby"
+ app_file "lib/tasks/task.rake", "# TODO: note in rake"
boot_rails
require 'rake'
@@ -45,8 +46,9 @@ module ApplicationTests
assert_match(/note in js/, output)
assert_match(/note in css/, output)
assert_match(/note in scss/, output)
+ assert_match(/note in rake/, output)
- assert_equal 8, lines.size
+ assert_equal 9, lines.size
lines.each do |line|
assert_equal 4, line[0].size
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index b05fe3aed5..c5a68a5152 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -46,7 +46,7 @@ module ApplicationTests
end
rake_tasks do
- task :do_nothing => :environment do
+ task do_nothing: :environment do
end
end
RUBY
@@ -60,7 +60,7 @@ module ApplicationTests
config.eager_load = true
rake_tasks do
- task :do_nothing => :environment do
+ task do_nothing: :environment do
Hello.new.world
end
end
@@ -86,12 +86,12 @@ module ApplicationTests
def test_rake_test_error_output
Dir.chdir(app_path){ `rake db:migrate` }
- app_file "test/unit/one_unit_test.rb", <<-RUBY
- raise 'unit'
+ app_file "test/models/one_model_test.rb", <<-RUBY
+ raise 'models'
RUBY
- app_file "test/functional/one_functional_test.rb", <<-RUBY
- raise 'functional'
+ app_file "test/controllers/one_controller_test.rb", <<-RUBY
+ raise 'controllers'
RUBY
app_file "test/integration/one_integration_test.rb", <<-RUBY
@@ -100,8 +100,8 @@ module ApplicationTests
silence_stderr do
output = Dir.chdir(app_path) { `rake test 2>&1` }
- assert_match 'unit', output
- assert_match 'functional', output
+ assert_match 'models', output
+ assert_match 'controllers', output
assert_match 'integration', output
end
end
@@ -109,7 +109,7 @@ module ApplicationTests
def test_rake_routes_calls_the_route_inspector
app_file "config/routes.rb", <<-RUBY
AppTemplate::Application.routes.draw do
- get '/cart', :to => 'cart#show'
+ get '/cart', to: 'cart#show'
end
RUBY
assert_equal "cart GET /cart(.:format) cart#show\n", Dir.chdir(app_path){ `rake routes` }
@@ -118,7 +118,7 @@ module ApplicationTests
def test_logger_is_flushed_when_exiting_production_rake_tasks
add_to_config <<-RUBY
rake_tasks do
- task :log_something => :environment do
+ task log_something: :environment do
Rails.logger.error("Sample log message")
end
end
@@ -233,7 +233,7 @@ module ApplicationTests
app_file "lib/tasks/count_user.rake", <<-RUBY
namespace :user do
- task :count => :environment do
+ task count: :environment do
puts User.count
end
end
diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb
index 396b1849d8..ffcdeac7f0 100644
--- a/railties/test/application/routing_test.rb
+++ b/railties/test/application/routing_test.rb
@@ -50,7 +50,7 @@ module ApplicationTests
controller :foo, <<-RUBY
class FooController < ApplicationController
def index
- render :inline => "<%= foo_or_bar? %>"
+ render inline: "<%= foo_or_bar? %>"
end
end
RUBY
@@ -76,7 +76,7 @@ module ApplicationTests
test "mount rack app" do
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
- mount lambda { |env| [200, {}, [env["PATH_INFO"]]] }, :at => "/blog"
+ mount lambda { |env| [200, {}, [env["PATH_INFO"]]] }, at: "/blog"
# The line below is required because mount sometimes
# fails when a resource route is added.
resource :user
@@ -91,7 +91,7 @@ module ApplicationTests
controller :foo, <<-RUBY
class FooController < ApplicationController
def index
- render :text => "foo"
+ render text: "foo"
end
end
RUBY
@@ -99,7 +99,7 @@ module ApplicationTests
controller :bar, <<-RUBY
class BarController < ActionController::Base
def index
- render :text => "bar"
+ render text: "bar"
end
end
RUBY
@@ -121,7 +121,7 @@ module ApplicationTests
controller 'foo', <<-RUBY
class FooController < ApplicationController
def index
- render :text => "foo"
+ render text: "foo"
end
end
RUBY
@@ -130,7 +130,7 @@ module ApplicationTests
module Admin
class FooController < ApplicationController
def index
- render :text => "admin::foo"
+ render text: "admin::foo"
end
end
end
@@ -138,8 +138,8 @@ module ApplicationTests
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
- get 'admin/foo', :to => 'admin/foo#index'
- get 'foo', :to => 'foo#index'
+ get 'admin/foo', to: 'admin/foo#index'
+ get 'foo', to: 'foo#index'
end
RUBY
@@ -183,18 +183,18 @@ module ApplicationTests
controller :foo, <<-RUBY
class FooController < ApplicationController
def bar
- render :text => "bar"
+ render text: "bar"
end
def baz
- render :text => "baz"
+ render text: "baz"
end
end
RUBY
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
- get 'foo', :to => 'foo#bar'
+ get 'foo', to: 'foo#bar'
end
RUBY
@@ -205,7 +205,7 @@ module ApplicationTests
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
- get 'foo', :to => 'foo#baz'
+ get 'foo', to: 'foo#baz'
end
RUBY
@@ -226,7 +226,7 @@ module ApplicationTests
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
- get 'foo', :to => ::InitializeRackApp
+ get 'foo', to: ::InitializeRackApp
end
RUBY
@@ -257,7 +257,7 @@ module ApplicationTests
controller 'yazilar', <<-RUBY
class YazilarController < ApplicationController
def index
- render :text => 'yazilar#index'
+ render text: 'yazilar#index'
end
end
RUBY
diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb
index f7e60749a7..2a48adae5c 100644
--- a/railties/test/application/url_generation_test.rb
+++ b/railties/test/application/url_generation_test.rb
@@ -15,8 +15,9 @@ module ApplicationTests
class MyApp < Rails::Application
config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
- config.session_store :cookie_store, :key => "_myapp_session"
+ config.session_store :cookie_store, key: "_myapp_session"
config.active_support.deprecation = :log
+ config.eager_load = false
end
MyApp.initialize!
@@ -26,12 +27,12 @@ module ApplicationTests
class ::OmgController < ::ApplicationController
def index
- render :text => omg_path
+ render text: omg_path
end
end
MyApp.routes.draw do
- get "/" => "omg#index", :as => :omg
+ get "/" => "omg#index", as: :omg
end
require 'rack/test'
diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb
index 91ede1cb68..e047d4882d 100644
--- a/railties/test/commands/console_test.rb
+++ b/railties/test/commands/console_test.rb
@@ -58,8 +58,8 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
end
def test_console_defaults_to_IRB
- config = mock("config", :console => nil)
- app = mock("app", :config => config)
+ config = mock("config", console: nil)
+ app = mock("app", config: config)
app.expects(:load_console).returns(nil)
assert_equal IRB, Rails::Console.new(app).console
@@ -68,40 +68,40 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
def test_default_environment_with_no_rails_env
with_rails_env nil do
start
- assert_match /\sdevelopment\s/, output
+ assert_match(/\sdevelopment\s/, output)
end
end
def test_default_environment_with_rails_env
with_rails_env 'special-production' do
start
- assert_match /\sspecial-production\s/, output
+ assert_match(/\sspecial-production\s/, output)
end
end
def test_e_option
start ['-e', 'special-production']
- assert_match /\sspecial-production\s/, output
+ assert_match(/\sspecial-production\s/, output)
end
def test_environment_option
start ['--environment=special-production']
- assert_match /\sspecial-production\s/, output
+ assert_match(/\sspecial-production\s/, output)
end
def test_rails_env_is_production_when_first_argument_is_p
start ['p']
- assert_match /\sproduction\s/, output
+ assert_match(/\sproduction\s/, output)
end
def test_rails_env_is_test_when_first_argument_is_t
start ['t']
- assert_match /\stest\s/, output
+ assert_match(/\stest\s/, output)
end
def test_rails_env_is_development_when_argument_is_d
start ['d']
- assert_match /\sdevelopment\s/, output
+ assert_match(/\sdevelopment\s/, output)
end
private
@@ -115,8 +115,8 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
def app
@app ||= begin
- config = mock("config", :console => FakeConsole)
- app = mock("app", :config => config)
+ config = mock("config", console: FakeConsole)
+ app = mock("app", config: config)
app.stubs(:sandbox=).returns(nil)
app.expects(:load_console)
app
diff --git a/railties/test/fixtures/lib/plugin_builders/spec_builder.rb b/railties/test/fixtures/lib/plugin_builders/spec_builder.rb
index aa18c7ddaa..2721429599 100644
--- a/railties/test/fixtures/lib/plugin_builders/spec_builder.rb
+++ b/railties/test/fixtures/lib/plugin_builders/spec_builder.rb
@@ -4,7 +4,7 @@ class PluginBuilder < Rails::PluginBuilder
append_file "Rakefile", <<-EOF
# spec tasks in rakefile
-task :default => :spec
+task default: :spec
EOF
end
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index bc086c5986..8af92479c3 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -99,7 +99,7 @@ class ActionsTest < Rails::Generators::TestCase
def test_environment_should_include_data_in_environment_initializer_block_with_env_option
run_generator
autoload_paths = 'config.autoload_paths += %w["#{Rails.root}/app/extras"]'
- action :environment, autoload_paths, :env => 'development'
+ action :environment, autoload_paths, env: 'development'
assert_file "config/environments/development.rb", /Application\.configure do\n #{Regexp.escape(autoload_paths)}/
end
@@ -124,7 +124,7 @@ class ActionsTest < Rails::Generators::TestCase
def test_git_with_hash_should_run_each_command_using_git_scm
generator.expects(:run).times(2)
- action :git, :rm => 'README', :add => '.'
+ action :git, rm: 'README', add: '.'
end
def test_vendor_should_write_data_to_file_in_vendor
@@ -138,8 +138,8 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_rakefile_should_write_date_to_file_in_lib_tasks
- action :rakefile, 'myapp.rake', 'task :run => [:environment]'
- assert_file 'lib/tasks/myapp.rake', 'task :run => [:environment]'
+ action :rakefile, 'myapp.rake', 'task run: [:environment]'
+ assert_file 'lib/tasks/myapp.rake', 'task run: [:environment]'
end
def test_initializer_should_write_date_to_file_in_config_initializers
@@ -148,12 +148,12 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_generate_should_run_script_generate_with_argument_and_options
- generator.expects(:run_ruby_script).once.with('script/rails generate model MyModel', :verbose => false)
+ generator.expects(:run_ruby_script).once.with('script/rails generate model MyModel', verbose: false)
action :generate, 'model', 'MyModel'
end
def test_rake_should_run_rake_command_with_default_env
- generator.expects(:run).once.with("rake log:clear RAILS_ENV=development", :verbose => false)
+ generator.expects(:run).once.with("rake log:clear RAILS_ENV=development", verbose: false)
old_env, ENV['RAILS_ENV'] = ENV["RAILS_ENV"], nil
action :rake, 'log:clear'
ensure
@@ -161,12 +161,12 @@ class ActionsTest < Rails::Generators::TestCase
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'
+ generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', verbose: false)
+ action :rake, 'log:clear', env: 'production'
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)
+ generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', verbose: false)
old_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "production"
action :rake, 'log:clear'
ensure
@@ -174,29 +174,29 @@ class ActionsTest < Rails::Generators::TestCase
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)
+ generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', verbose: false)
old_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "staging"
- action :rake, 'log:clear', :env => 'production'
+ action :rake, 'log:clear', env: 'production'
ensure
ENV["RAILS_ENV"] = old_env
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)
+ generator.expects(:run).once.with("sudo rake log:clear RAILS_ENV=development", verbose: false)
old_env, ENV['RAILS_ENV'] = ENV["RAILS_ENV"], nil
- action :rake, 'log:clear', :sudo => true
+ action :rake, 'log:clear', sudo: true
ensure
ENV["RAILS_ENV"] = old_env
end
def test_capify_should_run_the_capify_command
- generator.expects(:run).once.with('capify .', :verbose => false)
+ generator.expects(:run).once.with('capify .', verbose: false)
action :capify!
end
def test_route_should_add_data_to_the_routes_block_in_config_routes
run_generator
- route_command = "route '/login', :controller => 'sessions', :action => 'new'"
+ route_command = "route '/login', controller: 'sessions', action: 'new'"
action :route, route_command
assert_file 'config/routes.rb', /#{Regexp.escape(route_command)}/
end
@@ -208,7 +208,7 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_readme_with_quiet
- generator(default_arguments, :quiet => true)
+ generator(default_arguments, quiet: true)
run_generator
Rails::Generators::AppGenerator.expects(:source_root).times(2).returns(destination_root)
assert_no_match(/Welcome to Rails/, action(:readme, "README.rdoc"))
@@ -223,12 +223,12 @@ class ActionsTest < Rails::Generators::TestCase
end
def test_log_with_quiet
- generator(default_arguments, :quiet => true)
+ generator(default_arguments, quiet: true)
assert_equal("", action(:log, "YES"))
end
def test_log_with_status_with_quiet
- generator(default_arguments, :quiet => true)
+ generator(default_arguments, quiet: true)
assert_equal("", action(:log, :yes, "YES"))
end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 856a0e163b..62e1f3531e 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -26,10 +26,12 @@ DEFAULT_APP_FILES = %w(
log
script/rails
test/fixtures
- test/functional
+ test/controllers
+ test/models
+ test/helpers
+ test/mailers
test/integration
test/performance
- test/unit
vendor
vendor/assets
tmp/cache
@@ -105,8 +107,8 @@ class AppGeneratorTest < Rails::Generators::TestCase
FileUtils.mv(app_root, app_moved_root)
- generator = Rails::Generators::AppGenerator.new ["rails"], { :with_dispatchers => true },
- :destination_root => app_moved_root, :shell => @shell
+ generator = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true },
+ destination_root: app_moved_root, shell: @shell
generator.send(:app_const)
quietly { generator.send(:create_config_files) }
assert_file "myapp_moved/config/environment.rb", /Myapp::Application\.initialize!/
@@ -121,7 +123,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
Rails.application.class.stubs(:name).returns("Myapp")
Rails.application.stubs(:is_a?).returns(Rails::Application)
- generator = Rails::Generators::AppGenerator.new ["rails"], { :with_dispatchers => true }, :destination_root => app_root, :shell => @shell
+ generator = Rails::Generators::AppGenerator.new ["rails"], { with_dispatchers: true }, destination_root: app_root, shell: @shell
generator.send(:app_const)
quietly { generator.send(:create_config_files) }
assert_file "myapp/config/initializers/session_store.rb", /_myapp_session/
@@ -221,7 +223,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_generator_if_skip_sprockets_is_given
run_generator [destination_root, "--skip-sprockets"]
assert_file "config/application.rb" do |content|
- assert_match(/#\s+require\s+["']sprockets\/rails\/railtie["']/, content)
+ assert_match(/#\s+require\s+["']sprockets\/railtie["']/, content)
assert_no_match(/config\.assets\.enabled = true/, content)
end
assert_file "Gemfile" do |content|
@@ -234,7 +236,8 @@ class AppGeneratorTest < Rails::Generators::TestCase
end
assert_file "config/environments/production.rb" do |content|
assert_no_match(/config\.assets\.digest = true/, content)
- assert_no_match(/config\.assets\.compress = true/, content)
+ assert_no_match(/config\.assets\.js_compressor = :uglifier/, content)
+ assert_no_match(/config\.assets\.css_compressor = :sass/, content)
end
assert_file "test/performance/browsing_test.rb"
end
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
index c3fa9ebb03..5205deafd9 100644
--- a/railties/test/generators/controller_generator_test.rb
+++ b/railties/test/generators/controller_generator_test.rb
@@ -28,13 +28,13 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
def test_invokes_helper
run_generator
assert_file "app/helpers/account_helper.rb"
- assert_file "test/unit/helpers/account_helper_test.rb"
+ assert_file "test/helpers/account_helper_test.rb"
end
def test_does_not_invoke_helper_if_required
run_generator ["account", "--skip-helper"]
assert_no_file "app/helpers/account_helper.rb"
- assert_no_file "test/unit/helpers/account_helper_test.rb"
+ assert_no_file "test/helpers/account_helper_test.rb"
end
def test_invokes_assets
@@ -45,12 +45,12 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/functional/account_controller_test.rb"
+ assert_file "test/controllers/account_controller_test.rb"
end
def test_does_not_invoke_test_framework_if_required
run_generator ["account", "--no-test-framework"]
- assert_no_file "test/functional/account_controller_test.rb"
+ assert_no_file "test/controllers/account_controller_test.rb"
end
def test_invokes_default_template_engine
diff --git a/railties/test/generators/helper_generator_test.rb b/railties/test/generators/helper_generator_test.rb
index 8da3aa61a4..81d4fcb129 100644
--- a/railties/test/generators/helper_generator_test.rb
+++ b/railties/test/generators/helper_generator_test.rb
@@ -15,7 +15,7 @@ class HelperGeneratorTest < Rails::Generators::TestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/unit/helpers/admin_helper_test.rb", /class AdminHelperTest < ActionView::TestCase/
+ assert_file "test/helpers/admin_helper_test.rb", /class AdminHelperTest < ActionView::TestCase/
end
def test_logs_if_the_test_framework_cannot_be_found
diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb
index c501780e7f..6b2351fc1a 100644
--- a/railties/test/generators/mailer_generator_test.rb
+++ b/railties/test/generators/mailer_generator_test.rb
@@ -29,7 +29,7 @@ class MailerGeneratorTest < Rails::Generators::TestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/functional/notifier_test.rb" do |test|
+ assert_file "test/mailers/notifier_test.rb" do |test|
assert_match(/class NotifierTest < ActionMailer::TestCase/, test)
assert_match(/test "foo"/, test)
assert_match(/test "bar"/, test)
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 436de26826..0c7ff0ebe7 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -157,7 +157,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_match(/create_table :products/, up)
assert_match(/t\.string :name/, up)
assert_match(/t\.integer :supplier_id/, up)
-
+
assert_match(/add_index :products, :name/, up)
assert_match(/add_index :products, :supplier_id/, up)
assert_no_match(/add_index :products, :year/, up)
@@ -181,7 +181,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_match(/add_index :products, :discount, unique: true/, content)
end
end
-
+
def test_migration_without_timestamps
ActiveRecord::Base.timestamped_migrations = false
run_generator ["account"]
@@ -242,19 +242,19 @@ class ModelGeneratorTest < Rails::Generators::TestCase
def test_migration_is_skipped_on_skip_behavior
run_generator
- output = run_generator ["Account"], :behavior => :skip
+ output = run_generator ["Account"], behavior: :skip
assert_match %r{skip\s+db/migrate/\d+_create_accounts.rb}, output
end
def test_migration_error_is_not_shown_on_revoke
run_generator
- error = capture(:stderr){ run_generator ["Account"], :behavior => :revoke }
+ error = capture(:stderr){ run_generator ["Account"], behavior: :revoke }
assert_no_match(/Another migration is already named create_accounts/, error)
end
def test_migration_is_removed_on_revoke
run_generator
- run_generator ["Account"], :behavior => :revoke
+ run_generator ["Account"], behavior: :revoke
assert_no_migration "db/migrate/create_accounts.rb"
end
@@ -269,7 +269,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/unit/account_test.rb", /class AccountTest < ActiveSupport::TestCase/
+ assert_file "test/models/account_test.rb", /class AccountTest < ActiveSupport::TestCase/
assert_file "test/fixtures/accounts.yml", /name: MyString/, /age: 1/
end
diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
index db2b8af217..4b168ae110 100644
--- a/railties/test/generators/namespaced_generators_test.rb
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -25,7 +25,7 @@ class NamespacedControllerGeneratorTest < NamespacedGeneratorTestCase
/module TestApp/,
/ class AccountController < ApplicationController/
- assert_file "test/functional/test_app/account_controller_test.rb",
+ assert_file "test/controllers/test_app/account_controller_test.rb",
/module TestApp/,
/ class AccountControllerTest/
end
@@ -46,12 +46,12 @@ class NamespacedControllerGeneratorTest < NamespacedGeneratorTestCase
def test_helpr_is_also_namespaced
run_generator
assert_file "app/helpers/test_app/account_helper.rb", /module TestApp/, / module AccountHelper/
- assert_file "test/unit/helpers/test_app/account_helper_test.rb", /module TestApp/, / class AccountHelperTest/
+ assert_file "test/helpers/test_app/account_helper_test.rb", /module TestApp/, / class AccountHelperTest/
end
def test_invokes_default_test_framework
run_generator
- assert_file "test/functional/test_app/account_controller_test.rb"
+ assert_file "test/controllers/test_app/account_controller_test.rb"
end
def test_invokes_default_template_engine
@@ -136,7 +136,7 @@ class NamespacedModelGeneratorTest < NamespacedGeneratorTestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/unit/test_app/account_test.rb", /module TestApp/, /class AccountTest < ActiveSupport::TestCase/
+ assert_file "test/models/test_app/account_test.rb", /module TestApp/, /class AccountTest < ActiveSupport::TestCase/
assert_file "test/fixtures/test_app/accounts.yml", /name: MyString/, /age: 1/
end
end
@@ -158,7 +158,7 @@ class NamespacedObserverGeneratorTest < NamespacedGeneratorTestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/unit/test_app/account_observer_test.rb", /module TestApp/, / class AccountObserverTest < ActiveSupport::TestCase/
+ assert_file "test/models/test_app/account_observer_test.rb", /module TestApp/, / class AccountObserverTest < ActiveSupport::TestCase/
end
end
@@ -186,7 +186,7 @@ class NamespacedMailerGeneratorTest < NamespacedGeneratorTestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/functional/test_app/notifier_test.rb" do |test|
+ assert_file "test/mailers/test_app/notifier_test.rb" do |test|
assert_match(/module TestApp/, test)
assert_match(/class NotifierTest < ActionMailer::TestCase/, test)
assert_match(/test "foo"/, test)
@@ -225,7 +225,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Model
assert_file "app/models/test_app/product_line.rb", /module TestApp\n class ProductLine < ActiveRecord::Base/
- assert_file "test/unit/test_app/product_line_test.rb", /module TestApp\n class ProductLineTest < ActiveSupport::TestCase/
+ assert_file "test/models/test_app/product_line_test.rb", /module TestApp\n class ProductLineTest < ActiveSupport::TestCase/
assert_file "test/fixtures/test_app/product_lines.yml"
assert_migration "db/migrate/create_test_app_product_lines.rb"
@@ -240,7 +240,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
/module TestApp/,
/class ProductLinesController < ApplicationController/
- assert_file "test/functional/test_app/product_lines_controller_test.rb",
+ assert_file "test/controllers/test_app/product_lines_controller_test.rb",
/module TestApp\n class ProductLinesControllerTest < ActionController::TestCase/
# Views
@@ -255,7 +255,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Helpers
assert_file "app/helpers/test_app/product_lines_helper.rb"
- assert_file "test/unit/helpers/test_app/product_lines_helper_test.rb"
+ assert_file "test/helpers/test_app/product_lines_helper_test.rb"
# Stylesheets
assert_file "app/assets/stylesheets/scaffold.css"
@@ -263,11 +263,11 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
def test_scaffold_on_revoke
run_generator
- run_generator ["product_line"], :behavior => :revoke
+ run_generator ["product_line"], behavior: :revoke
# Model
assert_no_file "app/models/test_app/product_line.rb"
- assert_no_file "test/unit/test_app/product_line_test.rb"
+ assert_no_file "test/models/test_app/product_line_test.rb"
assert_no_file "test/fixtures/test_app/product_lines.yml"
assert_no_migration "db/migrate/create_test_app_product_lines.rb"
@@ -278,7 +278,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Controller
assert_no_file "app/controllers/test_app/product_lines_controller.rb"
- assert_no_file "test/functional/test_app/product_lines_controller_test.rb"
+ assert_no_file "test/controllers/test_app/product_lines_controller_test.rb"
# Views
assert_no_file "app/views/test_app/product_lines"
@@ -286,7 +286,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Helpers
assert_no_file "app/helpers/test_app/product_lines_helper.rb"
- assert_no_file "test/unit/helpers/test_app/product_lines_helper_test.rb"
+ assert_no_file "test/helpers/test_app/product_lines_helper_test.rb"
# Stylesheets (should not be removed)
assert_file "app/assets/stylesheets/scaffold.css"
@@ -298,13 +298,13 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Model
assert_file "app/models/test_app/admin.rb", /module TestApp\n module Admin/
assert_file "app/models/test_app/admin/role.rb", /module TestApp\n class Admin::Role < ActiveRecord::Base/
- assert_file "test/unit/test_app/admin/role_test.rb", /module TestApp\n class Admin::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/models/test_app/admin/role_test.rb", /module TestApp\n class Admin::RoleTest < ActiveSupport::TestCase/
assert_file "test/fixtures/test_app/admin/roles.yml"
assert_migration "db/migrate/create_test_app_admin_roles.rb"
# Route
assert_file "config/routes.rb" do |route|
- assert_match(/namespace :admin do resources :roles end$/, route)
+ assert_match(/^ namespace :admin do\n resources :roles\n end$/, route)
end
# Controller
@@ -312,7 +312,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
assert_match(/module TestApp\n class Admin::RolesController < ApplicationController/, content)
end
- assert_file "test/functional/test_app/admin/roles_controller_test.rb",
+ assert_file "test/controllers/test_app/admin/roles_controller_test.rb",
/module TestApp\n class Admin::RolesControllerTest < ActionController::TestCase/
# Views
@@ -327,7 +327,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Helpers
assert_file "app/helpers/test_app/admin/roles_helper.rb"
- assert_file "test/unit/helpers/test_app/admin/roles_helper_test.rb"
+ assert_file "test/helpers/test_app/admin/roles_helper_test.rb"
# Stylesheets
assert_file "app/assets/stylesheets/scaffold.css"
@@ -335,23 +335,23 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
def test_scaffold_with_namespace_on_revoke
run_generator [ "admin/role", "name:string", "description:string" ]
- run_generator [ "admin/role" ], :behavior => :revoke
+ run_generator [ "admin/role" ], behavior: :revoke
# Model
assert_file "app/models/test_app/admin.rb" # ( should not be remove )
assert_no_file "app/models/test_app/admin/role.rb"
- assert_no_file "test/unit/test_app/admin/role_test.rb"
+ assert_no_file "test/models/test_app/admin/role_test.rb"
assert_no_file "test/fixtures/test_app/admin/roles.yml"
assert_no_migration "db/migrate/create_test_app_admin_roles.rb"
# Route
assert_file "config/routes.rb" do |route|
- assert_no_match(/namespace :admin do resources :roles end$/, route)
+ assert_no_match(/^ namespace :admin do\n resources :roles\n end$$/, route)
end
# Controller
assert_no_file "app/controllers/test_app/admin/roles_controller.rb"
- assert_no_file "test/functional/test_app/admin/roles_controller_test.rb"
+ assert_no_file "test/controllers/test_app/admin/roles_controller_test.rb"
# Views
assert_no_file "app/views/test_app/admin/roles"
@@ -359,7 +359,79 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Helpers
assert_no_file "app/helpers/test_app/admin/roles_helper.rb"
- assert_no_file "test/unit/helpers/test_app/admin/roles_helper_test.rb"
+ assert_no_file "test/helpers/test_app/admin/roles_helper_test.rb"
+
+ # Stylesheets (should not be removed)
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
+
+ def test_scaffold_with_nested_namespace_on_invoke
+ run_generator [ "admin/user/special/role", "name:string", "description:string" ]
+
+ # Model
+ assert_file "app/models/test_app/admin/user/special.rb", /module TestApp\n module Admin/
+ assert_file "app/models/test_app/admin/user/special/role.rb", /module TestApp\n class Admin::User::Special::Role < ActiveRecord::Base/
+ assert_file "test/models/test_app/admin/user/special/role_test.rb", /module TestApp\n class Admin::User::Special::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/test_app/admin/user/special/roles.yml"
+ assert_migration "db/migrate/create_test_app_admin_user_special_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/^ namespace :admin do\n namespace :user do\n namespace :special do\n resources :roles\n end\n end\n end$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/test_app/admin/user/special/roles_controller.rb" do |content|
+ assert_match(/module TestApp\n class Admin::User::Special::RolesController < ApplicationController/, content)
+ end
+
+ assert_file "test/controllers/test_app/admin/user/special/roles_controller_test.rb",
+ /module TestApp\n class Admin::User::Special::RolesControllerTest < ActionController::TestCase/
+
+ # Views
+ %w(
+ index
+ edit
+ new
+ show
+ _form
+ ).each { |view| assert_file "app/views/test_app/admin/user/special/roles/#{view}.html.erb" }
+ assert_no_file "app/views/layouts/admin/user/special/roles.html.erb"
+
+ # Helpers
+ assert_file "app/helpers/test_app/admin/user/special/roles_helper.rb"
+ assert_file "test/helpers/test_app/admin/user/special/roles_helper_test.rb"
+
+ # Stylesheets
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
+
+ def test_scaffold_with_nested_namespace_on_revoke
+ run_generator [ "admin/user/special/role", "name:string", "description:string" ]
+ run_generator [ "admin/user/special/role" ], behavior: :revoke
+
+ # Model
+ assert_file "app/models/test_app/admin/user/special.rb" # ( should not be remove )
+ assert_no_file "app/models/test_app/admin/user/special/role.rb"
+ assert_no_file "test/models/test_app/admin/user/special/role_test.rb"
+ assert_no_file "test/fixtures/test_app/admin/user/special/roles.yml"
+ assert_no_migration "db/migrate/create_test_app_admin_user_special_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_no_match(/^ namespace :admin do\n namespace :user do\n namespace :special do\n resources :roles\n end\n end\n end$/, route)
+ end
+
+ # Controller
+ assert_no_file "app/controllers/test_app/admin/user/special/roles_controller.rb"
+ assert_no_file "test/controllers/test_app/admin/user/special/roles_controller_test.rb"
+
+ # Views
+ assert_no_file "app/views/test_app/admin/user/special/roles"
+
+ # Helpers
+ assert_no_file "app/helpers/test_app/admin/user/special/roles_helper.rb"
+ assert_no_file "test/helpers/test_app/admin/user/special/roles_helper_test.rb"
# Stylesheets (should not be removed)
assert_file "app/assets/stylesheets/scaffold.css"
diff --git a/railties/test/generators/observer_generator_test.rb b/railties/test/generators/observer_generator_test.rb
index afcee0a2dc..1231827466 100644
--- a/railties/test/generators/observer_generator_test.rb
+++ b/railties/test/generators/observer_generator_test.rb
@@ -17,7 +17,7 @@ class ObserverGeneratorTest < Rails::Generators::TestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/unit/account_observer_test.rb", /class AccountObserverTest < ActiveSupport::TestCase/
+ assert_file "test/models/account_observer_test.rb", /class AccountObserverTest < ActiveSupport::TestCase/
end
def test_logs_if_the_test_framework_cannot_be_found
diff --git a/railties/test/generators/orm_test.rb b/railties/test/generators/orm_test.rb
index 9dd3d3e0ec..88ae930554 100644
--- a/railties/test/generators/orm_test.rb
+++ b/railties/test/generators/orm_test.rb
@@ -20,12 +20,12 @@ class OrmTest < Rails::Generators::TestCase
tests Rails::Generators::ScaffoldControllerGenerator
def test_orm_class_returns_custom_generator_if_supported_custom_orm_set
- g = generator ["Foo"], :orm => "ORMWithGenerators"
+ g = generator ["Foo"], orm: "ORMWithGenerators"
assert_equal ORMWithGenerators::Generators::ActiveModel, g.send(:orm_class)
end
def test_orm_class_returns_rails_generator_if_unsupported_custom_orm_set
- g = generator ["Foo"], :orm => "ORMWithoutGenerators"
+ g = generator ["Foo"], orm: "ORMWithoutGenerators"
assert_equal Rails::Generators::ActiveModel, g.send(:orm_class)
end
diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb
index dddbfa64b6..6974db5751 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -379,7 +379,7 @@ class CustomPluginGeneratorTest < Rails::Generators::TestCase
run_generator([destination_root, "-b", "#{Rails.root}/lib/plugin_builders/spec_builder.rb"])
assert_file 'spec/spec_helper.rb'
assert_file 'spec/dummy'
- assert_file 'Rakefile', /task :default => :spec/
+ assert_file 'Rakefile', /task default: :spec/
assert_file 'Rakefile', /# spec tasks in rakefile/
end
diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb
index 73804dae45..3d4e694361 100644
--- a/railties/test/generators/resource_generator_test.rb
+++ b/railties/test/generators/resource_generator_test.rb
@@ -18,7 +18,7 @@ class ResourceGeneratorTest < Rails::Generators::TestCase
%w(
app/models/account.rb
- test/unit/account_test.rb
+ test/models/account_test.rb
test/fixtures/accounts.yml
).each { |path| assert_file path }
@@ -33,10 +33,10 @@ class ResourceGeneratorTest < Rails::Generators::TestCase
def test_resource_controller_with_pluralized_class_name
run_generator
assert_file "app/controllers/accounts_controller.rb", /class AccountsController < ApplicationController/
- assert_file "test/functional/accounts_controller_test.rb", /class AccountsControllerTest < ActionController::TestCase/
+ assert_file "test/controllers/accounts_controller_test.rb", /class AccountsControllerTest < ActionController::TestCase/
assert_file "app/helpers/accounts_helper.rb", /module AccountsHelper/
- assert_file "test/unit/helpers/accounts_helper_test.rb", /class AccountsHelperTest < ActionView::TestCase/
+ assert_file "test/helpers/accounts_helper_test.rb", /class AccountsHelperTest < ActionView::TestCase/
end
def test_resource_controller_with_actions
@@ -62,14 +62,14 @@ class ResourceGeneratorTest < Rails::Generators::TestCase
def test_plural_names_are_singularized
content = run_generator ["accounts".freeze]
assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/
- assert_file "test/unit/account_test.rb", /class AccountTest/
+ assert_file "test/models/account_test.rb", /class AccountTest/
assert_match(/Plural version of the model detected, using singularized version. Override with --force-plural./, content)
end
def test_plural_names_can_be_forced
content = run_generator ["accounts", "--force-plural"]
assert_file "app/models/accounts.rb", /class Accounts < ActiveRecord::Base/
- assert_file "test/unit/accounts_test.rb", /class AccountsTest/
+ assert_file "test/models/accounts_test.rb", /class AccountsTest/
assert_no_match(/Plural version of the model detected/, content)
end
@@ -80,7 +80,7 @@ class ResourceGeneratorTest < Rails::Generators::TestCase
def test_route_is_removed_on_revoke
run_generator
- run_generator ["account"], :behavior => :revoke
+ run_generator ["account"], behavior: :revoke
assert_file "config/routes.rb" do |route|
assert_no_match(/resources :accounts$/, route)
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index aa09343346..38454dfb8b 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -57,7 +57,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
def test_helper_are_invoked_with_a_pluralized_name
run_generator
assert_file "app/helpers/users_helper.rb", /module UsersHelper/
- assert_file "test/unit/helpers/users_helper_test.rb", /class UsersHelperTest < ActionView::TestCase/
+ assert_file "test/helpers/users_helper_test.rb", /class UsersHelperTest < ActionView::TestCase/
end
def test_views_are_generated
@@ -75,7 +75,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
def test_functional_tests
run_generator
- assert_file "test/functional/users_controller_test.rb" do |content|
+ 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, user: \{ age: @user.age, name: @user.name \}/, content)
@@ -86,7 +86,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
def test_functional_tests_without_attributes
run_generator ["User"]
- assert_file "test/functional/users_controller_test.rb" do |content|
+ 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, user: \{ \}/, content)
@@ -97,7 +97,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
def test_skip_helper_if_required
run_generator ["User", "name:string", "age:integer", "--no-helper"]
assert_no_file "app/helpers/users_helper.rb"
- assert_no_file "test/unit/helpers/users_helper_test.rb"
+ assert_no_file "test/helpers/users_helper_test.rb"
end
def test_skip_layout_if_required
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 40c5188042..efe47cdfcb 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -12,7 +12,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Model
assert_file "app/models/product_line.rb", /class ProductLine < ActiveRecord::Base/
- assert_file "test/unit/product_line_test.rb", /class ProductLineTest < ActiveSupport::TestCase/
+ 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/
@@ -60,7 +60,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
end
end
- assert_file "test/functional/product_lines_controller_test.rb" do |test|
+ assert_file "test/controllers/product_lines_controller_test.rb" do |test|
assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, test)
assert_match(/post :create, product_line: \{ title: @product_line.title \}/, test)
assert_match(/put :update, id: @product_line, product_line: \{ title: @product_line.title \}/, test)
@@ -78,7 +78,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Helpers
assert_file "app/helpers/product_lines_helper.rb"
- assert_file "test/unit/helpers/product_lines_helper_test.rb"
+ assert_file "test/helpers/product_lines_helper_test.rb"
# Assets
assert_file "app/assets/stylesheets/scaffold.css"
@@ -89,7 +89,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
def test_functional_tests_without_attributes
run_generator ["product_line"]
- assert_file "test/functional/product_lines_controller_test.rb" do |content|
+ assert_file "test/controllers/product_lines_controller_test.rb" do |content|
assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, content)
assert_match(/test "should get index"/, content)
assert_match(/post :create, product_line: \{ \}/, content)
@@ -99,11 +99,11 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
def test_scaffold_on_revoke
run_generator
- run_generator ["product_line"], :behavior => :revoke
+ run_generator ["product_line"], behavior: :revoke
# Model
assert_no_file "app/models/product_line.rb"
- assert_no_file "test/unit/product_line_test.rb"
+ assert_no_file "test/models/product_line_test.rb"
assert_no_file "test/fixtures/product_lines.yml"
assert_no_migration "db/migrate/create_product_lines.rb"
@@ -114,7 +114,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Controller
assert_no_file "app/controllers/product_lines_controller.rb"
- assert_no_file "test/functional/product_lines_controller_test.rb"
+ assert_no_file "test/controllers/product_lines_controller_test.rb"
# Views
assert_no_file "app/views/product_lines"
@@ -122,7 +122,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Helpers
assert_no_file "app/helpers/product_lines_helper.rb"
- assert_no_file "test/unit/helpers/product_lines_helper_test.rb"
+ assert_no_file "test/helpers/product_lines_helper_test.rb"
# Assets
assert_file "app/assets/stylesheets/scaffold.css", /:visited/
@@ -136,13 +136,13 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Model
assert_file "app/models/admin.rb", /module Admin/
assert_file "app/models/admin/role.rb", /class Admin::Role < ActiveRecord::Base/
- assert_file "test/unit/admin/role_test.rb", /class Admin::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/models/admin/role_test.rb", /class Admin::RoleTest < ActiveSupport::TestCase/
assert_file "test/fixtures/admin/roles.yml"
assert_migration "db/migrate/create_admin_roles.rb"
# Route
assert_file "config/routes.rb" do |route|
- assert_match(/namespace :admin do resources :roles end$/, route)
+ assert_match(/^ namespace :admin do\n resources :roles\n end$/, route)
end
# Controller
@@ -183,7 +183,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
end
end
- assert_file "test/functional/admin/roles_controller_test.rb",
+ assert_file "test/controllers/admin/roles_controller_test.rb",
/class Admin::RolesControllerTest < ActionController::TestCase/
# Views
@@ -198,7 +198,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Helpers
assert_file "app/helpers/admin/roles_helper.rb"
- assert_file "test/unit/helpers/admin/roles_helper_test.rb"
+ assert_file "test/helpers/admin/roles_helper_test.rb"
# Assets
assert_file "app/assets/stylesheets/scaffold.css", /:visited/
@@ -213,7 +213,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Model
assert_file "app/models/admin.rb" # ( should not be remove )
assert_no_file "app/models/admin/role.rb"
- assert_no_file "test/unit/admin/role_test.rb"
+ assert_no_file "test/models/admin/role_test.rb"
assert_no_file "test/fixtures/admin/roles.yml"
assert_no_migration "db/migrate/create_admin_roles.rb"
@@ -224,7 +224,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Controller
assert_no_file "app/controllers/admin/roles_controller.rb"
- assert_no_file "test/functional/admin/roles_controller_test.rb"
+ assert_no_file "test/controllers/admin/roles_controller_test.rb"
# Views
assert_no_file "app/views/admin/roles"
@@ -232,7 +232,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Helpers
assert_no_file "app/helpers/admin/roles_helper.rb"
- assert_no_file "test/unit/helpers/admin/roles_helper_test.rb"
+ assert_no_file "test/helpers/admin/roles_helper_test.rb"
# Assets
assert_file "app/assets/stylesheets/scaffold.css"
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index a4bdfcf438..1e5a4545a1 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -87,7 +87,7 @@ module SharedGeneratorTests
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)
+ generator([destination_root], template: path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template)
assert_match(/It works!/, capture(:stdout) { generator.invoke_all })
end
@@ -96,31 +96,31 @@ module SharedGeneratorTests
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)
+ generator([destination_root], template: path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template)
assert_match(/It works!/, capture(:stdout) { generator.invoke_all })
end
def test_dev_option
- generator([destination_root], :dev => true).expects(:bundle_command).with('install').once
+ generator([destination_root], dev: true).expects(:bundle_command).with('install').once
quietly { generator.invoke_all }
rails_path = File.expand_path('../../..', Rails.root)
assert_file 'Gemfile', /^gem\s+["']rails["'],\s+path:\s+["']#{Regexp.escape(rails_path)}["']$/
end
def test_edge_option
- generator([destination_root], :edge => true).expects(:bundle_command).with('install').once
+ generator([destination_root], edge: true).expects(:bundle_command).with('install').once
quietly { generator.invoke_all }
assert_file 'Gemfile', %r{^gem\s+["']rails["'],\s+github:\s+["']#{Regexp.escape("rails/rails")}["']$}
end
def test_skip_gemfile
- generator([destination_root], :skip_gemfile => true).expects(:bundle_command).never
+ generator([destination_root], skip_gemfile: true).expects(:bundle_command).never
quietly { generator.invoke_all }
assert_no_file 'Gemfile'
end
def test_skip_bundle
- generator([destination_root], :skip_bundle => true).expects(:bundle_command).never
+ generator([destination_root], skip_bundle: true).expects(:bundle_command).never
quietly { generator.invoke_all }
# skip_bundle is only about running bundle install, ensure the Gemfile is still
@@ -192,7 +192,7 @@ module SharedCustomGeneratorTests
template = "class #{builder_class}; end"
template.instance_eval "def read; self; end" # Make the string respond to read
- generator([destination_root], :builder => url).expects(:open).with(url, 'Accept' => 'application/x-thor-template').returns(template)
+ generator([destination_root], builder: url).expects(:open).with(url, 'Accept' => 'application/x-thor-template').returns(template)
quietly { generator.invoke_all }
default_files.each{ |path| assert_no_file(path) }
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 027d8eb9b7..9953aa929b 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -43,8 +43,8 @@ class GeneratorsTest < Rails::Generators::TestCase
end
def test_invoke_with_config_values
- Rails::Generators::ModelGenerator.expects(:start).with(["Account"], :behavior => :skip)
- Rails::Generators.invoke :model, ["Account"], :behavior => :skip
+ Rails::Generators::ModelGenerator.expects(:start).with(["Account"], behavior: :skip)
+ Rails::Generators.invoke :model, ["Account"], behavior: :skip
end
def test_find_by_namespace
@@ -165,7 +165,7 @@ class GeneratorsTest < Rails::Generators::TestCase
end
def test_developer_options_are_overwriten_by_user_options
- Rails::Generators.options[:with_options] = { :generate => false }
+ Rails::Generators.options[:with_options] = { generate: false }
self.class.class_eval(<<-end_eval, __FILE__, __LINE__ + 1)
class WithOptionsGenerator < Rails::Generators::Base
@@ -186,7 +186,7 @@ class GeneratorsTest < Rails::Generators::TestCase
File.open(template, 'w'){ |f| f.write "empty" }
capture(:stdout) do
- Rails::Generators.invoke :model, ["user"], :destination_root => destination_root
+ Rails::Generators.invoke :model, ["user"], destination_root: destination_root
end
assert_file "app/models/user.rb" do |content|
diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb
index c84c7f204c..16e259be5d 100644
--- a/railties/test/initializable_test.rb
+++ b/railties/test/initializable_test.rb
@@ -43,17 +43,17 @@ module InitializableTests
class Child < Parent
include Rails::Initializable
- initializer :three, :before => :one do
+ initializer :three, before: :one do
$arr << 3
end
- initializer :four, :after => :one, :before => :two do
+ initializer :four, after: :one, before: :two do
$arr << 4
end
end
class Parent
- initializer :five, :before => :one do
+ initializer :five, before: :one do
$arr << 5
end
end
@@ -61,7 +61,7 @@ module InitializableTests
class Instance
include Rails::Initializable
- initializer :one, :group => :assets do
+ initializer :one, group: :assets do
$arr << 1
end
@@ -69,7 +69,7 @@ module InitializableTests
$arr << 2
end
- initializer :three, :group => :all do
+ initializer :three, group: :all do
$arr << 3
end
@@ -90,11 +90,11 @@ module InitializableTests
class MoreInitializers
include Rails::Initializable
- initializer :startup, :before => :last do
+ initializer :startup, before: :last do
$arr << 3
end
- initializer :terminate, :after => :first, :before => :startup do
+ initializer :terminate, after: :first, before: :startup do
$arr << two
end
@@ -134,11 +134,11 @@ module InitializableTests
class PluginB
include Rails::Initializable
- initializer "plugin_b.startup", :after => "plugin_a.startup" do
+ initializer "plugin_b.startup", after: "plugin_a.startup" do
$arr << 2
end
- initializer "plugin_b.terminate", :before => "plugin_a.terminate" do
+ initializer "plugin_b.terminate", before: "plugin_a.terminate" do
$arr << 3
end
end
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 0f36eb67e5..e59488f97d 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -113,14 +113,14 @@ module TestHelpers
routes = File.read("#{app_path}/config/routes.rb")
if routes =~ /(\n\s*end\s*)\Z/
File.open("#{app_path}/config/routes.rb", 'w') do |f|
- f.puts $` + "\nmatch ':controller(/:action(/:id))(.:format)', :via => :all\n" + $1
+ f.puts $` + "\nmatch ':controller(/:action(/:id))(.:format)', via: :all\n" + $1
end
end
add_to_config <<-RUBY
config.eager_load = false
config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
- config.session_store :cookie_store, :key => "_myapp_session"
+ config.session_store :cookie_store, key: "_myapp_session"
config.active_support.deprecation = :log
config.action_controller.allow_forgery_protection = false
RUBY
@@ -139,7 +139,7 @@ module TestHelpers
app = Class.new(Rails::Application)
app.config.eager_load = false
app.config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
- app.config.session_store :cookie_store, :key => "_myapp_session"
+ app.config.session_store :cookie_store, key: "_myapp_session"
app.config.active_support.deprecation = :log
yield app if block_given?
@@ -157,7 +157,7 @@ module TestHelpers
controller :foo, <<-RUBY
class FooController < ApplicationController
def index
- render :text => "foo"
+ render text: "foo"
end
end
RUBY
diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb
index 76ff3ec3e4..12f18b9dbf 100644
--- a/railties/test/paths_test.rb
+++ b/railties/test/paths_test.rb
@@ -32,7 +32,7 @@ class PathsTest < ActiveSupport::TestCase
end
test "creating a root level path with options" do
- @root.add "app", :with => "/foo/bar"
+ @root.add "app", with: "/foo/bar"
assert_equal ["/foo/bar"], @root["app"].to_a
end
@@ -52,18 +52,18 @@ class PathsTest < ActiveSupport::TestCase
test "creating a child level path with option" do
@root.add "app"
- @root.add "app/models", :with => "/foo/bar/baz"
+ @root.add "app/models", with: "/foo/bar/baz"
assert_equal ["/foo/bar/baz"], @root["app/models"].to_a
end
test "child level paths are relative from the root" do
@root.add "app"
- @root.add "app/models", :with => "baz"
+ @root.add "app/models", with: "baz"
assert_equal ["/foo/bar/baz"], @root["app/models"].to_a
end
test "adding multiple physical paths as an array" do
- @root.add "app", :with => ["/app", "/app2"]
+ @root.add "app", with: ["/app", "/app2"]
assert_equal ["/app", "/app2"], @root["app"].to_a
end
@@ -92,7 +92,7 @@ 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.add "app", with: "/app"
@root["app"].autoload_once!
assert @root["app"].autoload_once?
assert @root.autoload_once.include?(@root["app"].expanded.first)
@@ -109,13 +109,13 @@ 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
+ @root.add "app", with: "/app", autoload_once: true
assert @root["app"].autoload_once?
assert @root.autoload_once.include?("/app")
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
+ @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")
@@ -153,20 +153,20 @@ 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
+ @root.add "app", with: "/app", eager_load: true
assert @root["app"].eager_load?
assert @root.eager_load.include?("/app")
end
test "it is possible to add multiple paths without assignment and mark them as eager" do
- @root.add "app", :with => ["/app", "/app2"], :eager_load => true
+ @root.add "app", with: ["/app", "/app2"], eager_load: true
assert @root["app"].eager_load?
assert @root.eager_load.include?("/app")
assert @root.eager_load.include?("/app2")
end
test "it is possible to create a path without assignment and mark it both as eager and load once" do
- @root.add "app", :with => "/app", :eager_load => true, :autoload_once => true
+ @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")
@@ -194,12 +194,12 @@ class PathsTest < ActiveSupport::TestCase
end
test "it should be possible to override a path's default glob without assignment" do
- @root.add "app", :with => "/app", :glob => "*.rb"
+ @root.add "app", with: "/app", glob: "*.rb"
assert_equal "*.rb", @root["app"].glob
end
test "it should be possible to replace a path and persist the original paths glob" do
- @root.add "app", :glob => "*.rb"
+ @root.add "app", glob: "*.rb"
@root["app"] = "app2"
assert_equal ["/foo/bar/app2"], @root["app"].to_a
assert_equal "*.rb", @root["app"].glob
@@ -213,7 +213,7 @@ class PathsTest < ActiveSupport::TestCase
end
test "a path can be added to the load path on creation" do
- @root.add "app", :with => "/app", :load_path => true
+ @root.add "app", with: "/app", load_path: true
assert @root["app"].load_path?
assert_equal ["/app"], @root.load_paths
end
@@ -226,7 +226,7 @@ class PathsTest < ActiveSupport::TestCase
end
test "a path can be marked as autoload on creation" do
- @root.add "app", :with => "/app", :autoload => true
+ @root.add "app", with: "/app", autoload: true
assert @root["app"].autoload?
assert_equal ["/app"], @root.autoload_paths
end
diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb
index cfb32b7d35..08fcddd4bf 100644
--- a/railties/test/rails_info_controller_test.rb
+++ b/railties/test/rails_info_controller_test.rb
@@ -21,19 +21,19 @@ class InfoControllerTest < ActionController::TestCase
end
test "info controller does not allow remote requests" do
- @controller.stubs(:local_request? => false)
+ @controller.stubs(local_request?: false)
get :properties
assert_response :forbidden
end
test "info controller renders an error message when request was forbidden" do
- @controller.stubs(:local_request? => false)
+ @controller.stubs(local_request?: false)
get :properties
assert_select 'p'
end
test "info controller allows requests when all requests are considered local" do
- @controller.stubs(:local_request? => true)
+ @controller.stubs(local_request?: true)
get :properties
assert_response :success
end
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index e52b3efdab..fcbe7b6cda 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -285,8 +285,8 @@ module RailtiesTest
@plugin.write "config/routes.rb", <<-RUBY
Rails.application.routes.draw do
- get 'foo', :to => 'bar#index'
- get 'bar', :to => 'bar#index'
+ get 'foo', to: 'bar#index'
+ get 'bar', to: 'bar#index'
end
RUBY
@@ -366,7 +366,7 @@ YAML
Rails.application.routes.draw do
namespace :admin do
namespace :foo do
- get "bar", :to => "bar#index"
+ get "bar", to: "bar#index"
end
end
end
@@ -375,7 +375,7 @@ YAML
@plugin.write "app/controllers/admin/foo/bar_controller.rb", <<-RUBY
class Admin::Foo::BarController < ApplicationController
def index
- render :text => "Rendered from namespace"
+ render text: "Rendered from namespace"
end
end
RUBY
@@ -487,14 +487,14 @@ YAML
controller "foo", <<-RUBY
class FooController < ActionController::Base
def index
- render :text => params[:username]
+ render text: params[:username]
end
end
RUBY
@plugin.write "config/routes.rb", <<-RUBY
Bukkits::Engine.routes.draw do
- root :to => "foo#index"
+ root to: "foo#index"
end
RUBY
@@ -608,14 +608,14 @@ YAML
app_file "config/routes.rb", <<-RUBY
AppTemplate::Application.routes.draw do
- get "/bar" => "bar#index", :as => "bar"
- mount Bukkits::Engine => "/bukkits", :as => "bukkits"
+ get "/bar" => "bar#index", as: "bar"
+ mount Bukkits::Engine => "/bukkits", as: "bukkits"
end
RUBY
@plugin.write "config/routes.rb", <<-RUBY
Bukkits::Engine.routes.draw do
- get "/foo" => "foo#index", :as => "foo"
+ get "/foo" => "foo#index", as: "foo"
get "/foo/show" => "foo#show"
get "/from_app" => "foo#from_app"
get "/routes_helpers_in_view" => "foo#routes_helpers_in_view"
@@ -643,23 +643,23 @@ YAML
@plugin.write "app/controllers/bukkits/foo_controller.rb", <<-RUBY
class Bukkits::FooController < ActionController::Base
def index
- render :inline => "<%= help_the_engine %>"
+ render inline: "<%= help_the_engine %>"
end
def show
- render :text => foo_path
+ render text: foo_path
end
def from_app
- render :inline => "<%= (self.respond_to?(:bar_path) || self.respond_to?(:something)) %>"
+ render inline: "<%= (self.respond_to?(:bar_path) || self.respond_to?(:something)) %>"
end
def routes_helpers_in_view
- render :inline => "<%= foo_path %>, <%= main_app.bar_path %>"
+ render inline: "<%= foo_path %>, <%= main_app.bar_path %>"
end
def polymorphic_path_without_namespace
- render :text => polymorphic_path(Post.new)
+ render text: polymorphic_path(Post.new)
end
end
RUBY
@@ -726,7 +726,7 @@ YAML
app_file "config/routes.rb", <<-RUBY
AppTemplate::Application.routes.draw do
- mount Bukkits::Engine => "/bukkits", :as => "bukkits"
+ mount Bukkits::Engine => "/bukkits", as: "bukkits"
end
RUBY
@@ -1033,11 +1033,11 @@ YAML
controller "main", <<-RUBY
class MainController < ActionController::Base
def foo
- render :inline => '<%= render :partial => "shared/foo" %>'
+ render inline: '<%= render :partial => "shared/foo" %>'
end
def bar
- render :inline => '<%= render :partial => "shared/bar" %>'
+ render inline: '<%= render :partial => "shared/bar" %>'
end
end
RUBY
@@ -1113,7 +1113,7 @@ YAML
controller "main", <<-RUBY
class MainController < ActionController::Base
def foo
- render :inline => '<%= render :partial => "shared/foo" %>'
+ render inline: '<%= render :partial => "shared/foo" %>'
end
end
RUBY
@@ -1167,7 +1167,7 @@ YAML
fullpath: \#{request.fullpath}
path: \#{request.path}
TEXT
- render :text => text
+ render text: text
end
end
end
@@ -1205,7 +1205,7 @@ YAML
app_file "app/controllers/bar_controller.rb", <<-RUBY
class BarController < ApplicationController
def index
- render :text => bukkits.bukkit_path
+ render text: bukkits.bukkit_path
end
end
RUBY
@@ -1227,7 +1227,7 @@ YAML
@plugin.write "app/controllers/bukkits/bukkit_controller.rb", <<-RUBY
class Bukkits::BukkitController < ActionController::Base
def index
- render :text => main_app.bar_path
+ render text: main_app.bar_path
end
end
RUBY
diff --git a/railties/test/railties/generators_test.rb b/railties/test/railties/generators_test.rb
index c90b795d59..0abb2b7578 100644
--- a/railties/test/railties/generators_test.rb
+++ b/railties/test/railties/generators_test.rb
@@ -46,7 +46,7 @@ module RailtiesTests
f.write <<-GEMFILE.gsub(/^ {12}/, '')
source "http://rubygems.org"
- gem 'rails', :path => '#{RAILS_FRAMEWORK_ROOT}'
+ gem 'rails', path: '#{RAILS_FRAMEWORK_ROOT}'
gem 'sqlite3'
GEMFILE
end
diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb
index bd13c3aba3..a1c52f01dc 100644
--- a/railties/test/railties/mounted_engine_test.rb
+++ b/railties/test/railties/mounted_engine_test.rb
@@ -42,14 +42,14 @@ module ApplicationTests
@simple_plugin.write "config/routes.rb", <<-RUBY
Weblog::Engine.routes.draw do
- get '/weblog' => "weblogs#index", :as => 'weblogs'
+ get '/weblog' => "weblogs#index", as: 'weblogs'
end
RUBY
@simple_plugin.write "app/controllers/weblogs_controller.rb", <<-RUBY
class WeblogsController < ActionController::Base
def index
- render :text => request.url
+ render text: request.url
end
end
RUBY
@@ -86,9 +86,9 @@ module ApplicationTests
@plugin.write "config/routes.rb", <<-RUBY
Blog::Engine.routes.draw do
resources :posts
- get '/generate_application_route', :to => 'posts#generate_application_route'
- get '/application_route_in_view', :to => 'posts#application_route_in_view'
- get '/engine_polymorphic_path', :to => 'posts#engine_polymorphic_path'
+ get '/generate_application_route', to: 'posts#generate_application_route'
+ get '/application_route_in_view', to: 'posts#application_route_in_view'
+ get '/engine_polymorphic_path', to: 'posts#engine_polymorphic_path'
end
RUBY
@@ -96,22 +96,22 @@ module ApplicationTests
module Blog
class PostsController < ActionController::Base
def index
- render :text => blog.post_path(1)
+ render text: blog.post_path(1)
end
def generate_application_route
- path = main_app.url_for(:controller => "/main",
- :action => "index",
- :only_path => true)
- render :text => path
+ path = main_app.url_for(controller: "/main",
+ action: "index",
+ only_path: true)
+ render text: path
end
def application_route_in_view
- render :inline => "<%= main_app.root_path %>"
+ render inline: "<%= main_app.root_path %>"
end
def engine_polymorphic_path
- render :text => polymorphic_path(Post.new)
+ render text: polymorphic_path(Post.new)
end
end
end
@@ -120,31 +120,31 @@ module ApplicationTests
app_file "app/controllers/application_generating_controller.rb", <<-RUBY
class ApplicationGeneratingController < ActionController::Base
def engine_route
- render :text => blog.posts_path
+ render text: blog.posts_path
end
def engine_route_in_view
- render :inline => "<%= blog.posts_path %>"
+ render inline: "<%= blog.posts_path %>"
end
def weblog_engine_route
- render :text => weblog.weblogs_path
+ render text: weblog.weblogs_path
end
def weblog_engine_route_in_view
- render :inline => "<%= weblog.weblogs_path %>"
+ render inline: "<%= weblog.weblogs_path %>"
end
def url_for_engine_route
- render :text => blog.url_for(:controller => "blog/posts", :action => "index", :user => "john", :only_path => true)
+ render text: blog.url_for(controller: "blog/posts", action: "index", user: "john", only_path: true)
end
def polymorphic_route
- render :text => polymorphic_url([blog, Blog::Post.new])
+ render text: polymorphic_url([blog, Blog::Post.new])
end
def application_polymorphic_path
- render :text => polymorphic_path(Blog::Post.new)
+ render text: polymorphic_path(Blog::Post.new)
end
end
RUBY
diff --git a/tools/profile b/tools/profile
index 6cc935bce5..d4cfa13f0a 100755
--- a/tools/profile
+++ b/tools/profile
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
# Example:
-# tools/profile_requires activesupport/lib/active_support.rb
+# tools/profile activesupport/lib/active_support.rb
abort 'Use REE so you can profile memory and object allocation' unless GC.respond_to?(:enable_stats)
ENV['NO_RELOAD'] ||= '1'